001: package liquibase.parser.visitor;
002:
003: import liquibase.ChangeSet;
004: import liquibase.FileOpener;
005: import liquibase.change.Change;
006: import liquibase.database.Database;
007: import liquibase.database.structure.Column;
008: import liquibase.database.structure.DatabaseObject;
009: import liquibase.database.structure.DatabaseSnapshot;
010: import liquibase.database.structure.Table;
011: import liquibase.dbdoc.*;
012: import liquibase.exception.DatabaseHistoryException;
013: import liquibase.exception.JDBCException;
014: import liquibase.exception.LiquibaseException;
015: import liquibase.util.StreamUtil;
016:
017: import java.io.File;
018: import java.io.FileOutputStream;
019: import java.io.IOException;
020: import java.io.InputStream;
021: import java.util.*;
022:
023: public class DBDocVisitor implements ChangeSetVisitor {
024:
025: private Database database;
026:
027: private SortedSet<ChangeLogInfo> changeLogs;
028: private Map<DatabaseObject, List<Change>> changesByObject;
029: private Map<String, List<Change>> changesByAuthor;
030:
031: private Map<DatabaseObject, List<Change>> changesToRunByObject;
032: private Map<String, List<Change>> changesToRunByAuthor;
033: private List<Change> changesToRun;
034: private List<Change> recentChanges;
035:
036: private String rootChangeLog;
037:
038: private static final int MAX_RECENT_CHANGE = 50;
039:
040: public DBDocVisitor(Database database) {
041: this .database = database;
042:
043: changesByObject = new HashMap<DatabaseObject, List<Change>>();
044: changesByAuthor = new HashMap<String, List<Change>>();
045: changeLogs = new TreeSet<ChangeLogInfo>();
046:
047: changesToRunByObject = new HashMap<DatabaseObject, List<Change>>();
048: changesToRunByAuthor = new HashMap<String, List<Change>>();
049: changesToRun = new ArrayList<Change>();
050: recentChanges = new ArrayList<Change>();
051: }
052:
053: public ChangeSetVisitor.Direction getDirection() {
054: return ChangeSetVisitor.Direction.FORWARD;
055: }
056:
057: public void visit(ChangeSet changeSet) throws LiquibaseException {
058: ChangeSet.RunStatus runStatus = database
059: .getRunStatus(changeSet);
060: if (rootChangeLog == null) {
061: rootChangeLog = changeSet.getFilePath();
062: }
063:
064: if (!changesByAuthor.containsKey(changeSet.getAuthor())) {
065: changesByAuthor.put(changeSet.getAuthor(),
066: new ArrayList<Change>());
067: }
068: if (!changesToRunByAuthor.containsKey(changeSet.getAuthor())) {
069: changesToRunByAuthor.put(changeSet.getAuthor(),
070: new ArrayList<Change>());
071: }
072:
073: boolean toRun = runStatus.equals(ChangeSet.RunStatus.NOT_RAN)
074: || runStatus.equals(ChangeSet.RunStatus.RUN_AGAIN);
075: for (Change change : changeSet.getChanges()) {
076: if (toRun) {
077: changesToRunByAuthor.get(changeSet.getAuthor()).add(
078: change);
079: changesToRun.add(change);
080: } else {
081: changesByAuthor.get(changeSet.getAuthor()).add(change);
082: recentChanges.add(0, change);
083: }
084: }
085:
086: ChangeLogInfo changeLogInfo = new ChangeLogInfo(changeSet
087: .getFilePath(), changeSet.getPhysicalFilePath());
088: if (!changeLogs.contains(changeLogInfo)) {
089: changeLogs.add(changeLogInfo);
090: }
091:
092: for (Change change : changeSet.getChanges()) {
093: Set<DatabaseObject> affectedDatabaseObjects = change
094: .getAffectedDatabaseObjects();
095: if (affectedDatabaseObjects != null) {
096: for (DatabaseObject dbObject : affectedDatabaseObjects) {
097: if (toRun) {
098: if (!changesToRunByObject.containsKey(dbObject)) {
099: changesToRunByObject.put(dbObject,
100: new ArrayList<Change>());
101: }
102: changesToRunByObject.get(dbObject).add(change);
103: }
104:
105: if (!changesByObject.containsKey(dbObject)) {
106: changesByObject.put(dbObject,
107: new ArrayList<Change>());
108: }
109: changesByObject.get(dbObject).add(change);
110: }
111: }
112: }
113: }
114:
115: public void writeHTML(File rootOutputDir, FileOpener fileOpener)
116: throws IOException, JDBCException, DatabaseHistoryException {
117: ChangeLogWriter changeLogWriter = new ChangeLogWriter(
118: fileOpener, rootOutputDir);
119: HTMLWriter authorWriter = new AuthorWriter(rootOutputDir,
120: database);
121: HTMLWriter tableWriter = new TableWriter(rootOutputDir,
122: database);
123: HTMLWriter columnWriter = new ColumnWriter(rootOutputDir,
124: database);
125: HTMLWriter pendingChangesWriter = new PendingChangesWriter(
126: rootOutputDir, database);
127: HTMLWriter recentChangesWriter = new RecentChangesWriter(
128: rootOutputDir, database);
129: HTMLWriter pendingSQLWriter = new PendingSQLWriter(
130: rootOutputDir, database);
131:
132: copyFile("liquibase/dbdoc/stylesheet.css", rootOutputDir);
133: copyFile("liquibase/dbdoc/index.html", rootOutputDir);
134: copyFile("liquibase/dbdoc/globalnav.html", rootOutputDir);
135: copyFile("liquibase/dbdoc/overview-summary.html", rootOutputDir);
136:
137: DatabaseSnapshot snapshot = new DatabaseSnapshot(database);
138:
139: new ChangeLogListWriter(rootOutputDir).writeHTML(changeLogs);
140: new TableListWriter(rootOutputDir)
141: .writeHTML(new TreeSet<Object>(snapshot.getTables()));
142: new AuthorListWriter(rootOutputDir)
143: .writeHTML(new TreeSet<Object>(changesByAuthor.keySet()));
144:
145: for (String author : changesByAuthor.keySet()) {
146: authorWriter.writeHTML(author, changesByAuthor.get(author),
147: changesToRunByAuthor.get(author), rootChangeLog);
148: }
149:
150: for (Table table : snapshot.getTables()) {
151: tableWriter.writeHTML(table, changesByObject.get(table),
152: changesToRunByObject.get(table), rootChangeLog);
153: }
154:
155: for (Column column : snapshot.getColumns()) {
156: columnWriter.writeHTML(column, changesByObject.get(column),
157: changesToRunByObject.get(column), rootChangeLog);
158: }
159:
160: for (ChangeLogInfo changeLog : changeLogs) {
161: changeLogWriter.writeChangeLog(changeLog.logicalPath,
162: changeLog.physicalPath);
163: }
164:
165: pendingChangesWriter.writeHTML("index", null, changesToRun,
166: rootChangeLog);
167: pendingSQLWriter.writeHTML("sql", null, changesToRun,
168: rootChangeLog);
169:
170: if (recentChanges.size() > MAX_RECENT_CHANGE) {
171: recentChanges = recentChanges.subList(0, MAX_RECENT_CHANGE);
172: }
173: recentChangesWriter.writeHTML("index", recentChanges, null,
174: rootChangeLog);
175:
176: }
177:
178: private void copyFile(String fileToCopy, File rootOutputDir)
179: throws IOException {
180: InputStream inputStream = getClass().getClassLoader()
181: .getResourceAsStream(fileToCopy);
182: FileOutputStream outputStream = null;
183: try {
184: if (inputStream == null) {
185: throw new IOException("Can not find " + fileToCopy);
186: }
187: outputStream = new FileOutputStream(new File(rootOutputDir,
188: fileToCopy.replaceFirst(".*\\/", "")), false);
189: StreamUtil.copy(inputStream, outputStream);
190: } finally {
191: if (outputStream != null) {
192: outputStream.close();
193: }
194: }
195: }
196:
197: private static class ChangeLogInfo implements
198: Comparable<ChangeLogInfo> {
199: public String logicalPath;
200: public String physicalPath;
201:
202: private ChangeLogInfo(String logicalPath, String physicalPath) {
203: this .logicalPath = logicalPath;
204: this .physicalPath = physicalPath;
205: }
206:
207: public boolean equals(Object o) {
208: if (this == o)
209: return true;
210: if (o == null || getClass() != o.getClass())
211: return false;
212:
213: ChangeLogInfo that = (ChangeLogInfo) o;
214:
215: return logicalPath.equals(that.logicalPath);
216:
217: }
218:
219: public int hashCode() {
220: return logicalPath.hashCode();
221: }
222:
223: public int compareTo(ChangeLogInfo o) {
224: return this .logicalPath.compareTo(o.logicalPath);
225: }
226:
227: public String toString() {
228: return logicalPath;
229: }
230: }
231: }
|