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.dependencyfinder.ant;
034:
035: import java.io.*;
036: import java.util.*;
037: import java.lang.reflect.*;
038:
039: import org.apache.tools.ant.*;
040: import org.apache.tools.ant.types.*;
041:
042: import com.jeantessier.classreader.*;
043: import com.jeantessier.diff.*;
044:
045: public class JarJarDiff extends Task {
046: public static final String API_STRATEGY = "api";
047: public static final String INCOMPATIBLE_STRATEGY = "incompatible";
048:
049: public static final String DEFAULT_LEVEL = API_STRATEGY;
050:
051: private String name = "";
052: private Path oldPath;
053: private String oldLabel;
054: private Path newPath;
055: private String newLabel;
056: private File filter;
057: private String level = DEFAULT_LEVEL;
058: private boolean code;
059: private String encoding = Report.DEFAULT_ENCODING;
060: private String dtdPrefix = Report.DEFAULT_DTD_PREFIX;
061: private String indentText;
062: private File destfile;
063:
064: public String getName() {
065: return name;
066: }
067:
068: public void setName(String name) {
069: this .name = name;
070: }
071:
072: public Path createOld() {
073: if (oldPath == null) {
074: oldPath = new Path(getProject());
075: }
076:
077: return oldPath;
078: }
079:
080: public Path getOld() {
081: return oldPath;
082: }
083:
084: public String getOldlabel() {
085: return oldLabel;
086: }
087:
088: public void setOldlabel(String oldLabel) {
089: this .oldLabel = oldLabel;
090: }
091:
092: public Path createNew() {
093: if (newPath == null) {
094: newPath = new Path(getProject());
095: }
096:
097: return newPath;
098: }
099:
100: public Path getNew() {
101: return newPath;
102: }
103:
104: public String getNewlabel() {
105: return newLabel;
106: }
107:
108: public void setNewlabel(String newLabel) {
109: this .newLabel = newLabel;
110: }
111:
112: public File getFilter() {
113: return filter;
114: }
115:
116: public void setfilter(File filter) {
117: this .filter = filter;
118: }
119:
120: public String getLevel() {
121: return level;
122: }
123:
124: public void setLevel(String level) {
125: this .level = level;
126: }
127:
128: public boolean getCode() {
129: return code;
130: }
131:
132: public void setCode(boolean code) {
133: this .code = code;
134: }
135:
136: public String getEncoding() {
137: return encoding;
138: }
139:
140: public void setEncoding(String encoding) {
141: this .encoding = encoding;
142: }
143:
144: public String getDtdprefix() {
145: return dtdPrefix;
146: }
147:
148: public void setDtdprefix(String dtdPrefix) {
149: this .dtdPrefix = dtdPrefix;
150: }
151:
152: public String getIndenttext() {
153: return indentText;
154: }
155:
156: public void setIntenttext(String indentText) {
157: this .indentText = indentText;
158: }
159:
160: public File getDestfile() {
161: return destfile;
162: }
163:
164: public void setDestfile(File destfile) {
165: this .destfile = destfile;
166: }
167:
168: public void execute() throws BuildException {
169: // first off, make sure that we've got what we need
170:
171: if (getOld() == null) {
172: throw new BuildException("old must be set!");
173: }
174:
175: if (getNew() == null) {
176: throw new BuildException("new must be set!");
177: }
178:
179: if (getDestfile() == null) {
180: throw new BuildException("destfile must be set!");
181: }
182:
183: VerboseListener verboseListener = new VerboseListener(this );
184:
185: try {
186: // Collecting data, first classfiles from JARs,
187: // then package/class trees using NodeFactory.
188:
189: log("Loading old classes from path " + getOld());
190: PackageMapper oldPackages = new PackageMapper();
191: ClassfileLoader oldJar = new AggregatingClassfileLoader();
192: oldJar.addLoadListener(oldPackages);
193: oldJar.addLoadListener(verboseListener);
194: oldJar.load(Arrays.asList(getOld().list()));
195:
196: log("Loading new classes from path " + getNew());
197: PackageMapper newPackages = new PackageMapper();
198: ClassfileLoader newJar = new AggregatingClassfileLoader();
199: newJar.addLoadListener(newPackages);
200: newJar.addLoadListener(verboseListener);
201: newJar.load(Arrays.asList(getNew().list()));
202:
203: DifferenceStrategy baseStrategy = getDefaultStrategy(getCode());
204: DifferenceStrategy strategy = getStrategy(getLevel(),
205: baseStrategy);
206:
207: if (getFilter() != null) {
208: strategy = new ListBasedDifferenceStrategy(strategy,
209: getFilter());
210: }
211:
212: // Starting to compare, first at package level,
213: // then descending to class level for packages
214: // that are in both the old and the new codebase.
215:
216: log("Comparing old and new classes ...");
217:
218: String name = getName();
219: String oldLabel = (getOldlabel() != null) ? getOldlabel()
220: : getOld().toString();
221: String newLabel = (getNewlabel() != null) ? getNewlabel()
222: : getNew().toString();
223:
224: DifferencesFactory factory = new DifferencesFactory(
225: strategy);
226: Differences differences = factory.createProjectDifferences(
227: name, oldLabel, oldPackages, newLabel, newPackages);
228:
229: log("Saving difference report to "
230: + getDestfile().getAbsolutePath());
231:
232: com.jeantessier.diff.Printer printer = new Report(
233: getEncoding(), getDtdprefix());
234: if (getIndenttext() != null) {
235: printer.setIndentText(getIndenttext());
236: }
237:
238: differences.accept(printer);
239:
240: PrintWriter out = new PrintWriter(new FileWriter(
241: getDestfile()));
242: out.print(printer);
243: out.close();
244: } catch (IOException ex) {
245: throw new BuildException(ex);
246: }
247: }
248:
249: private DifferenceStrategy getStrategy(String level,
250: DifferenceStrategy baseStrategy) {
251: DifferenceStrategy strategy;
252: if (API_STRATEGY.equals(level)) {
253: strategy = new APIDifferenceStrategy(baseStrategy);
254: } else if (INCOMPATIBLE_STRATEGY.equals(level)) {
255: strategy = new IncompatibleDifferenceStrategy(baseStrategy);
256: } else {
257: try {
258: Constructor constructor;
259: try {
260: constructor = Class.forName(level).getConstructor(
261: DifferenceStrategy.class);
262: strategy = (DifferenceStrategy) constructor
263: .newInstance(baseStrategy);
264: } catch (NoSuchMethodException ex) {
265: strategy = (DifferenceStrategy) Class
266: .forName(level).newInstance();
267: }
268: } catch (InvocationTargetException ex) {
269: log("Unknown level \"" + level
270: + "\", using default level \"" + DEFAULT_LEVEL
271: + "\": " + ex.getMessage());
272: strategy = getDefaultStrategy(baseStrategy);
273: } catch (InstantiationException ex) {
274: log("Unknown level \"" + level
275: + "\", using default level \"" + DEFAULT_LEVEL
276: + "\": " + ex.getMessage());
277: strategy = getDefaultStrategy(baseStrategy);
278: } catch (IllegalAccessException ex) {
279: log("Unknown level \"" + level
280: + "\", using default level \"" + DEFAULT_LEVEL
281: + "\": " + ex.getMessage());
282: strategy = getDefaultStrategy(baseStrategy);
283: } catch (ClassNotFoundException ex) {
284: log("Unknown level \"" + level
285: + "\", using default level \"" + DEFAULT_LEVEL
286: + "\": " + ex.getMessage());
287: strategy = getDefaultStrategy(baseStrategy);
288: } catch (ClassCastException ex) {
289: log("Unknown level \"" + level
290: + "\", using default level \"" + DEFAULT_LEVEL
291: + "\": " + ex.getMessage());
292: strategy = getDefaultStrategy(baseStrategy);
293: }
294: }
295: return strategy;
296: }
297:
298: private DifferenceStrategy getDefaultStrategy(boolean useCode) {
299: DifferenceStrategy baseStrategy;
300: if (useCode) {
301: baseStrategy = new CodeDifferenceStrategy();
302: } else {
303: baseStrategy = new NoDifferenceStrategy();
304: }
305: return baseStrategy;
306: }
307:
308: private APIDifferenceStrategy getDefaultStrategy(
309: DifferenceStrategy baseStrategy) {
310: return new APIDifferenceStrategy(baseStrategy);
311: }
312: }
|