001: //////////////////////////////////////////////////////////////////////////////
002: // Clirr: compares two versions of a java library for binary compatibility
003: // Copyright (C) 2003 - 2005 Lars Kühne
004: //
005: // This library is free software; you can redistribute it and/or
006: // modify it under the terms of the GNU Lesser General Public
007: // License as published by the Free Software Foundation; either
008: // version 2.1 of the License, or (at your option) any later version.
009: //
010: // This library is distributed in the hope that it will be useful,
011: // but WITHOUT ANY WARRANTY; without even the implied warranty of
012: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: // Lesser General Public License for more details.
014: //
015: // You should have received a copy of the GNU Lesser General Public
016: // License along with this library; if not, write to the Free Software
017: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: //////////////////////////////////////////////////////////////////////////////
019:
020: package net.sf.clirr.core;
021:
022: /**
023: * Describes an API change.
024: *
025: * @author Lars
026: */
027: public final class ApiDifference {
028: private static final int HASHCODE_MAGIC = 29;
029:
030: /**
031: * Object representing the message text to be output (or null if
032: * the constructor which takes a message string directly is used).
033: */
034: private Message message = null;
035:
036: /** human readable change report. */
037: private String report;
038:
039: /**
040: * severity of the change in terms of binary compatibility,
041: * as determined by clirr.
042: */
043: private Severity binaryCompatibilitySeverity;
044:
045: /**
046: * severity of the change in terms of source compatibility,
047: * as determined by clirr.
048: */
049: private Severity sourceCompatibilitySeverity;
050:
051: /** The fully qualified class name that is affected by the API change. */
052: private String affectedClass;
053:
054: /**
055: * The method that is affected, if any.
056: * <p/>
057: * The content is the method name plus the fully qualified
058: * parameter types separated by comma and space and enclosed in
059: * brackets, e.g. "doStuff(java.lang.String, int)".
060: * <p/>
061: * This value is <code>null</code> if no single method is
062: * affected, i.e. if the
063: * api change affects a field or is global
064: * (like "class is now final").
065: */
066: private String affectedMethod;
067:
068: /**
069: * The field that is affected, if any.
070: * <p/>
071: * The content is the field name, e.g. "someValue".
072: * Type information for the field is not available.
073: * <p/>
074: * This value is <code>null</code> if no single field is
075: * affected, i.e. if the
076: * api change affects a method or is global
077: * (like "class is now final").
078: */
079: private String affectedField;
080:
081: /**
082: * The set of additional parameters that are available for use
083: * when building the actual message description. These vary depending
084: * upon the actual difference being reported.
085: */
086: private String[] extraInfo;
087:
088: /**
089: * Invokes the two-severity-level version of this constructor.
090: */
091: public ApiDifference(Message message, Severity severity,
092: String clazz, String method, String field, String[] args) {
093: this (message, severity, severity, clazz, method, field, args);
094: }
095:
096: /**
097: * Create a new API difference representation.
098: *
099: * @param message is the key of a human readable string describing the
100: * change that was made.
101: *
102: * @param binarySeverity the severity in terms of binary compatibility,
103: * must be non-null.
104: *
105: * @param sourceSeverity the severity in terms of source code compatibility,
106: * must be non-null.
107: *
108: * @param clazz is the fully-qualified name of the class in which the
109: * change occurred, must be non-null.
110: *
111: * @param method the method signature of the method that changed,
112: * <code>null</code> if no method was affected.
113: *
114: * @param field the field name where the change occured, <code>null</code>
115: * if no field was affected.
116: *
117: * @param args is a set of additional change-specific strings which are
118: * made available for the message description string to reference via
119: * the standard {n} syntax.
120: */
121: public ApiDifference(Message message, Severity binarySeverity,
122: Severity sourceSeverity, String clazz, String method,
123: String field, String[] args) {
124: checkNonNull(message);
125: checkNonNull(binarySeverity);
126: checkNonNull(sourceSeverity);
127: checkNonNull(clazz);
128:
129: this .message = message;
130: this .binaryCompatibilitySeverity = binarySeverity;
131: this .sourceCompatibilitySeverity = sourceSeverity;
132: this .affectedClass = clazz;
133: this .affectedField = field;
134: this .affectedMethod = method;
135: this .extraInfo = args;
136: }
137:
138: /**
139: * Trivial utility method to verify that a specific object is non-null.
140: */
141: private void checkNonNull(Object o) {
142: if (o == null) {
143: throw new IllegalArgumentException();
144: }
145: }
146:
147: /**
148: * Return the message object (if any) associated with this difference.
149: * <p>
150: * Checks which support the "new" message API will provide ApiDifference
151: * objects with non-null message objects.
152: */
153: public Message getMessage() {
154: return message;
155: }
156:
157: /**
158: * The Severity of the API difference in terms of binary compatibility.
159: * ERROR means that clients will definitely break, WARNING means that
160: * clients may break, depending on how they use the library.
161: * See the eclipse paper for further explanation.
162: *
163: * @return the severity of the API difference in terms of binary compatibility.
164: */
165: public Severity getBinaryCompatibilitySeverity() {
166: return binaryCompatibilitySeverity;
167: }
168:
169: /**
170: * The Severity of the API difference in terms of source compatibility.
171: * Sometimes this is different than {@link #getBinaryCompatibilitySeverity
172: * binary compatibility severity}, for example adding a checked exception
173: * to a method signature is binary compatible but not source compatible.
174: * ERROR means that clients will definitely break, WARNING means that
175: * clients may break, depending on how they use the library.
176: * See the eclipse paper for further explanation.
177: *
178: * @return the severity of the API difference in terms of source code
179: * compatibility.
180: */
181: public Severity getSourceCompatibilitySeverity() {
182: return sourceCompatibilitySeverity;
183: }
184:
185: /**
186: * Return the maximum of the binary and source compatibility severities.
187: */
188: public Severity getMaximumSeverity() {
189: final Severity src = getSourceCompatibilitySeverity();
190: final Severity bin = getBinaryCompatibilitySeverity();
191: return src.compareTo(bin) < 0 ? bin : src;
192: }
193:
194: /**
195: * Human readable api change description.
196: *
197: * @return a human readable description of this API difference.
198: */
199: public String getReport(MessageTranslator translator) {
200: if (report != null) {
201: return report;
202: }
203:
204: String desc = translator.getDesc(message);
205: int nArgs = 0;
206: if (extraInfo != null) {
207: nArgs = extraInfo.length;
208: }
209: String[] strings = new String[nArgs + 3];
210: strings[0] = affectedClass;
211: strings[1] = affectedMethod;
212: strings[2] = affectedField;
213: for (int i = 0; i < nArgs; ++i) {
214: strings[i + 3] = extraInfo[i];
215: }
216:
217: return java.text.MessageFormat.format(desc, strings);
218: }
219:
220: /**
221: * The fully qualified class name of the class that has changed.
222: * @return fully qualified class name of the class that has changed.
223: */
224: public String getAffectedClass() {
225: return affectedClass;
226: }
227:
228: /**
229: * Method signature of the method that has changed, if any.
230: * @return method signature or <code>null</code> if no method is affected.
231: */
232: public String getAffectedMethod() {
233: return affectedMethod;
234: }
235:
236: /**
237: * Field name of the field that has changed, if any.
238: * @return field name or <code>null</code> if no field is affected.
239: */
240: public String getAffectedField() {
241: return affectedField;
242: }
243:
244: /**
245: * {@inheritDoc}
246: */
247: public String toString() {
248: StringBuffer buf = new StringBuffer();
249: buf.append(message.getId());
250: appendCommonData(buf);
251: return buf.toString();
252: }
253:
254: /**
255: * Get a human-readable description of this object. Intended for use by
256: * the unit tests.
257: */
258: public String toString(MessageTranslator translator) {
259: StringBuffer buf = new StringBuffer();
260: buf.append(getReport(translator));
261: appendCommonData(buf);
262: return buf.toString();
263: }
264:
265: /**
266: * Build a string containing a string representation of most of the
267: * fields in this object, but not the message-id or the string
268: * translation thereof.
269: */
270: private void appendCommonData(StringBuffer buf) {
271: buf.append(" (");
272: buf.append(binaryCompatibilitySeverity);
273:
274: if (sourceCompatibilitySeverity != binaryCompatibilitySeverity) {
275: buf.append(",");
276: buf.append(sourceCompatibilitySeverity);
277: }
278:
279: buf.append(") - ");
280: buf.append(affectedClass);
281: buf.append("[");
282: buf.append(affectedField);
283: buf.append("/");
284: buf.append(affectedMethod);
285: buf.append("]");
286: }
287: }
|