001: /*
002: * Copyright (c) 2001-2007, Jean Tessier
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions
007: * are met:
008: *
009: * * Redistributions of source code must retain the above copyright
010: * notice, this list of conditions and the following disclaimer.
011: *
012: * * Redistributions in binary form must reproduce the above copyright
013: * notice, this list of conditions and the following disclaimer in the
014: * documentation and/or other materials provided with the distribution.
015: *
016: * * Neither the name of Jean Tessier nor the names of his contributors
017: * may be used to endorse or promote products derived from this software
018: * without specific prior written permission.
019: *
020: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
021: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
022: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
023: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
024: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
027: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
028: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
029: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
030: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031: */
032:
033: package com.jeantessier.diff;
034:
035: import java.util.*;
036:
037: import org.apache.log4j.*;
038:
039: import com.jeantessier.classreader.*;
040:
041: public class DifferencesFactory {
042: private Classfile oldClass;
043: private Classfile newClass;
044:
045: private DifferenceStrategy strategy;
046:
047: /**
048: * For tests only.
049: */
050: DifferencesFactory() {
051: this (new APIDifferenceStrategy(new CodeDifferenceStrategy()));
052: }
053:
054: public DifferencesFactory(DifferenceStrategy strategy) {
055: this .strategy = strategy;
056: }
057:
058: public Differences createProjectDifferences(String name,
059: String oldVersion, PackageMapper oldPackages,
060: String newVersion, PackageMapper newPackages) {
061: Logger.getLogger(getClass()).debug(
062: "Begin " + name + " (" + oldVersion + " -> "
063: + newVersion + ")");
064:
065: ProjectDifferences projectDifferences = new ProjectDifferences(
066: name, oldVersion, newVersion);
067:
068: Logger.getLogger(getClass()).debug(
069: " Collecting packages ...");
070:
071: Collection<String> packageNames = new TreeSet<String>();
072: packageNames.addAll(oldPackages.getPackageNames());
073: packageNames.addAll(newPackages.getPackageNames());
074:
075: Logger.getLogger(getClass()).debug(
076: " Diff'ing packages ...");
077:
078: for (String packageName : packageNames) {
079: Map<String, Classfile> oldPackage = oldPackages
080: .getPackage(packageName);
081: if (oldPackage == null) {
082: oldPackage = Collections.EMPTY_MAP;
083: }
084:
085: Map<String, Classfile> newPackage = newPackages
086: .getPackage(packageName);
087: if (newPackage == null) {
088: newPackage = Collections.EMPTY_MAP;
089: }
090:
091: if (strategy.isPackageDifferent(oldPackage, newPackage)) {
092: projectDifferences.getPackageDifferences().add(
093: createPackageDifferences(packageName,
094: oldPackage, newPackage));
095: }
096: }
097:
098: Logger.getLogger(getClass()).debug(
099: "End " + name + " (" + oldVersion + " -> "
100: + newVersion + ")");
101:
102: return projectDifferences;
103: }
104:
105: public Differences createPackageDifferences(String name,
106: Map<String, Classfile> oldPackage,
107: Map<String, Classfile> newPackage) {
108: Logger.getLogger(getClass()).debug("Begin " + name);
109:
110: PackageDifferences packageDifferences = new PackageDifferences(
111: name, oldPackage, newPackage);
112:
113: if (oldPackage != null && !oldPackage.isEmpty()
114: && newPackage != null && !newPackage.isEmpty()) {
115: Logger.getLogger(getClass()).debug(
116: " Diff'ing classes ...");
117:
118: Collection<String> classNames = new TreeSet<String>();
119: classNames.addAll(oldPackage.keySet());
120: classNames.addAll(newPackage.keySet());
121:
122: for (String className : classNames) {
123: Classfile oldClass = oldPackage.get(className);
124: Classfile newClass = newPackage.get(className);
125:
126: if (strategy.isClassDifferent(oldClass, newClass)) {
127: packageDifferences.getClassDifferences().add(
128: createClassDifferences(className, oldClass,
129: newClass));
130: }
131: }
132:
133: Logger.getLogger(getClass()).debug(
134: " "
135: + name
136: + " has "
137: + packageDifferences.getClassDifferences()
138: .size()
139: + " class(es) that changed.");
140: }
141:
142: Logger.getLogger(getClass()).debug("End " + name);
143:
144: return packageDifferences;
145: }
146:
147: public Differences createClassDifferences(String name,
148: Classfile oldClass, Classfile newClass) {
149: Logger.getLogger(getClass()).debug("Begin " + name);
150:
151: ClassDifferences classDifferences;
152: if (((oldClass != null) && oldClass.isInterface())
153: || ((newClass != null) && newClass.isInterface())) {
154: classDifferences = new InterfaceDifferences(name, oldClass,
155: newClass);
156: } else {
157: classDifferences = new ClassDifferences(name, oldClass,
158: newClass);
159: }
160:
161: if (!classDifferences.isRemoved() && !classDifferences.isNew()
162: && strategy.isDeclarationModified(oldClass, newClass)) {
163: classDifferences.setDeclarationModified(true);
164: }
165:
166: Differences result = classDifferences;
167:
168: this .oldClass = oldClass;
169: this .newClass = newClass;
170:
171: if (oldClass != null && newClass != null) {
172: Logger.getLogger(getClass()).debug(
173: " Collecting fields ...");
174:
175: Map<String, String> fieldLevel = new TreeMap<String, String>();
176:
177: for (Field_info field : oldClass.getAllFields()) {
178: fieldLevel.put(field.getName(), field
179: .getFullSignature());
180: }
181:
182: for (Field_info field : newClass.getAllFields()) {
183: fieldLevel.put(field.getName(), field
184: .getFullSignature());
185: }
186:
187: Logger.getLogger(getClass()).debug(
188: " Diff'ing fields ...");
189:
190: for (String fieldName : fieldLevel.keySet()) {
191: String fieldFullName = fieldLevel.get(fieldName);
192:
193: Field_info oldField = oldClass.getField(fieldName);
194: Field_info newField = newClass.getField(fieldName);
195:
196: if (strategy.isFieldDifferent(oldField, newField)) {
197: classDifferences.getFeatureDifferences().add(
198: createFeatureDifferences(fieldFullName,
199: oldField, newField));
200: }
201: }
202:
203: Logger.getLogger(getClass()).debug(
204: " Collecting methods ...");
205:
206: Map<String, String> methodLevel = new TreeMap<String, String>();
207:
208: for (Method_info method : oldClass.getAllMethods()) {
209: methodLevel.put(method.getSignature(), method
210: .getFullSignature());
211: }
212:
213: for (Method_info method : newClass.getAllMethods()) {
214: methodLevel.put(method.getSignature(), method
215: .getFullSignature());
216: }
217:
218: Logger.getLogger(getClass()).debug(
219: " Diff'ing methods ...");
220:
221: for (String methodName : methodLevel.keySet()) {
222: String methodFullName = methodLevel.get(methodName);
223:
224: Method_info oldMethod = oldClass.getMethod(methodName);
225: Method_info newMethod = newClass.getMethod(methodName);
226:
227: if (strategy.isMethodDifferent(oldMethod, newMethod)) {
228: classDifferences.getFeatureDifferences().add(
229: createFeatureDifferences(methodFullName,
230: oldMethod, newMethod));
231: }
232: }
233:
234: Logger.getLogger(getClass()).debug(
235: name
236: + " has "
237: + classDifferences.getFeatureDifferences()
238: .size()
239: + " feature(s) that changed.");
240:
241: if (oldClass.isDeprecated() != newClass.isDeprecated()) {
242: result = new DeprecatableDifferences(result, oldClass,
243: newClass);
244: }
245: }
246:
247: Logger.getLogger(getClass()).debug("End " + name);
248:
249: return result;
250: }
251:
252: public Differences createFeatureDifferences(String name,
253: Feature_info oldFeature, Feature_info newFeature) {
254: Logger.getLogger(getClass()).debug("Begin " + name);
255:
256: FeatureDifferences featureDifferences;
257: if (oldFeature instanceof Field_info
258: || newFeature instanceof Field_info) {
259: featureDifferences = new FieldDifferences(name,
260: (Field_info) oldFeature, (Field_info) newFeature);
261:
262: if (!featureDifferences.isRemoved()
263: && !featureDifferences.isNew()
264: && strategy.isConstantValueDifferent(
265: ((Field_info) oldFeature)
266: .getConstantValue(),
267: ((Field_info) newFeature)
268: .getConstantValue())) {
269: ((FieldDifferences) featureDifferences)
270: .setConstantValueDifference(true);
271: }
272:
273: if (featureDifferences.isRemoved()
274: && newClass.locateField(name) != null) {
275: featureDifferences.setInherited(true);
276: }
277: } else {
278: if (((oldFeature instanceof Method_info) && ((Method_info) oldFeature)
279: .isConstructor())
280: || ((newFeature instanceof Method_info) && ((Method_info) newFeature)
281: .isConstructor())) {
282: featureDifferences = new ConstructorDifferences(name,
283: (Method_info) oldFeature,
284: (Method_info) newFeature);
285: } else {
286: featureDifferences = new MethodDifferences(name,
287: (Method_info) oldFeature,
288: (Method_info) newFeature);
289: }
290:
291: if (!featureDifferences.isRemoved()
292: && !featureDifferences.isNew()
293: && strategy.isCodeDifferent(
294: ((Method_info) oldFeature).getCode(),
295: ((Method_info) newFeature).getCode())) {
296: ((CodeDifferences) featureDifferences)
297: .setCodeDifference(true);
298: }
299:
300: if (featureDifferences.isRemoved()) {
301: Method_info attempt = newClass.locateMethod(name);
302: if ((attempt != null)
303: && (oldFeature.getClassfile().isInterface() == attempt
304: .getClassfile().isInterface())) {
305: featureDifferences.setInherited(true);
306: }
307: }
308: }
309:
310: Differences result = featureDifferences;
311:
312: if (oldFeature != null && newFeature != null) {
313: if (oldFeature.isDeprecated() != newFeature.isDeprecated()) {
314: result = new DeprecatableDifferences(result,
315: oldFeature, newFeature);
316: }
317: }
318:
319: Logger.getLogger(getClass()).debug("End " + name);
320:
321: return result;
322: }
323: }
|