001: /*
002: * The contents of this file are subject to the Mozilla Public License
003: * Version 1.1 (the "License"); you may not use this file except in
004: * compliance with the License. You may obtain a copy of the License at
005: * http://www.mozilla.org/MPL/
006: *
007: * Software distributed under the License is distributed on an "AS IS"
008: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
009: * License for the specific language governing rights and limitations
010: * under the License.
011: *
012: * The Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
013: *
014: * The Initial Developer of the Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
015: * Portions created by Mark A. Kobold are Copyright (C) 2000-2007. All Rights Reserved.
016: *
017: * Contributor(s):
018: * Mark A. Kobold [mkobold <at> isqlviewer <dot> com].
019: *
020: * If you didn't download this code from the following link, you should check
021: * if you aren't using an obsolete version: http://www.isqlviewer.com
022: */
023: package org.isqlviewer.history;
024:
025: import java.awt.datatransfer.DataFlavor;
026: import java.awt.datatransfer.Transferable;
027: import java.awt.datatransfer.UnsupportedFlavorException;
028: import java.io.IOException;
029: import java.io.Serializable;
030: import java.sql.SQLException;
031: import java.text.MessageFormat;
032: import java.util.Date;
033: import java.util.Enumeration;
034: import java.util.Vector;
035:
036: import org.isqlviewer.FieldNotModifiableException;
037: import org.isqlviewer.sql.embedded.EmbeddedDatabase;
038: import org.isqlviewer.util.Assertions;
039: import org.isqlviewer.util.BasicUtilities;
040:
041: /**
042: * Generic command object for logging command history within iSQL-Viewer.
043: * <p>
044: *
045: * @author Mark A. Kobold <mkobold at isqlviewer dot com>
046: * @version 1.0
047: */
048: public class HistoricalCommand implements Serializable, Transferable {
049:
050: /**
051: * Standard data-transfer flavor for DnD operations.
052: */
053: public static final DataFlavor FLAVOR = new DataFlavor(
054: HistoricalCommand.class, "iSQL-Viewer Historical Command");
055: private static final long serialVersionUID = -4661533009593760894L;
056:
057: private String commandText = null;
058: private String service = null;
059: private long id = -1;
060: private long transactionId = -1;
061: private CommandType type = CommandType.NOT_DEFINED;
062: private Date queryTime = null;
063: private Vector<HistoricalCommand> subCommands = new Vector<HistoricalCommand>();
064:
065: public Object getTransferData(DataFlavor flavor)
066: throws UnsupportedFlavorException, IOException {
067:
068: if (flavor != null) {
069: EmbeddedDatabase database = EmbeddedDatabase
070: .getSharedInstance();
071: HistoricalCommand command = null;
072: try {
073: if (hasChildCommands()) {
074: command = database.getHistoricalCommand(this );
075: } else {
076: command = database.getHistoricalCommand(getId());
077: }
078: } catch (SQLException sourceError) {
079: IOException wrappedError = new IOException(sourceError
080: .getMessage());
081: BasicUtilities.wrapThrowable(sourceError, wrappedError);
082: throw wrappedError;
083: }
084: StringBuilder userCommand = new StringBuilder();
085: if (hasChildCommands()) {
086: for (HistoricalCommand child : command.subCommands) {
087: userCommand.append(child.getCommandText());
088: userCommand.append(";\n");
089: }
090: command.setCommandText(userCommand.toString());
091: }
092: if (HistoricalCommand.FLAVOR.equals(flavor)) {
093: return command;
094: } else if (flavor.isFlavorTextType()) {
095: return command.getCommandText();
096: }
097: }
098: throw new UnsupportedFlavorException(flavor);
099: }
100:
101: public DataFlavor[] getTransferDataFlavors() {
102:
103: return new DataFlavor[] { FLAVOR, HistoricalCommand.FLAVOR,
104: DataFlavor.stringFlavor };
105: }
106:
107: public boolean isDataFlavorSupported(DataFlavor flavor) {
108:
109: if (flavor != null) {
110: if (FLAVOR.equals(flavor)
111: || HistoricalCommand.FLAVOR.equals(flavor)
112: || flavor.isFlavorTextType()) {
113: return true;
114: }
115: }
116: return false;
117: }
118:
119: @Override
120: public int hashCode() {
121:
122: return (int) (id ^ (id >>> 32));
123: }
124:
125: @Override
126: public boolean equals(Object other) {
127:
128: if (other instanceof HistoricalCommand) {
129: HistoricalCommand hr = (HistoricalCommand) other;
130: return hr.id == id;
131: }
132: return false;
133: }
134:
135: @Override
136: public String toString() {
137:
138: Object[] arguments = new Object[3];
139: arguments[0] = Long.toString(id);
140: arguments[1] = Long.toString(transactionId);
141: arguments[2] = type.name();
142: return MessageFormat
143: .format(
144: "HistoricalCommand[id=''{0}'', txnId=''{1}'', type=''{2}'']",
145: arguments);
146: }
147:
148: /**
149: * Gets the execution time at which this event occured.
150: * <p>
151: *
152: * @return timestamp at which this event happened as Java Date; can be null if not previously set.
153: */
154: public Date getQueryTime() {
155:
156: return queryTime == null ? null : (Date) queryTime.clone();
157: }
158:
159: /**
160: * Sets the time this command was executed at in millis.
161: * <p>
162: *
163: * @param queryTime as a Java date object.
164: * @throws NullPointerException if the given queryTime value is null.
165: */
166: public void setQueryTime(Date queryTime) {
167:
168: Assertions.assertNotNull("history-query-time", queryTime);
169: this .queryTime = (Date) queryTime.clone();
170: }
171:
172: /**
173: * Gets the unique identifier for this history reference.
174: * <p>
175: * Simply number that unique identifies this service reference within the iSQL-Viewer application. If the Id is -1
176: * or less than 0, then that indicates that this particular history reference instance is not registered within the
177: * application.
178: * <p>
179: * This value has no real value other than uniquely identifying this historical event.
180: *
181: * @return the id of this historical command; can be negative if not previously set.
182: */
183: public long getId() {
184:
185: return id;
186: }
187:
188: /**
189: * Sets the service reference identifier to the specified value.
190: * <p>
191: *
192: * @param id the id to set to uniquely identify this service.
193: * @throws IllegalArgumentException if the given id is not a positive number.
194: * @throws FieldNotModifiableException if the id for this instance has already been set.
195: */
196: public void setId(long id) {
197:
198: Assertions.assertPositive("history-reference-id", id);
199: if (this .id == -1) {
200: this .id = id;
201: } else {
202: throw new FieldNotModifiableException(
203: "history-reference-id");
204: }
205: }
206:
207: /**
208: * Gets the type of historical event this instance represents.
209: * <p>
210: *
211: * @return the type of command this instance is.
212: */
213: public CommandType getType() {
214:
215: return type;
216: }
217:
218: /**
219: * Sets the type of command this event represents.
220: * <p>
221: *
222: * @param type the event type for this instance.
223: * @throws NullPointerException if the given type is null.
224: * @throws FieldNotModifiableException if the type has been previously set.
225: */
226: public void setType(CommandType type) {
227:
228: Assertions.assertNotNull("history-reference-type", type);
229: if (this .type == CommandType.NOT_DEFINED) {
230: this .type = type;
231: } else {
232: throw new FieldNotModifiableException(
233: "history-reference-type");
234: }
235: }
236:
237: /**
238: * Gets the transaction id for this event.
239: * <p>
240: * If multiple events occur within the same transaction they will all be associated with the same transaction id.
241: * This id has no real value in its solitary form.
242: *
243: * @return the transactionId for this event; can be negative if not set or is not applicable to this event.
244: */
245: public long getTransactionId() {
246:
247: return transactionId;
248: }
249:
250: /**
251: * Sets the transactional id for this event .
252: * <p>
253: *
254: * @param transactionId to set for this event.
255: * @throws IllegalArgumentException if the given transaction id is negative; with the exception -1;
256: */
257: public void setTransactionId(long transactionId) {
258:
259: if (transactionId != -1) {
260: Assertions.assertPositive(
261: "history-reference-transaction-id", transactionId);
262: }
263: this .transactionId = transactionId;
264: }
265:
266: /**
267: * Gets the commandText of this command that was executed.
268: * <p>
269: *
270: * @return commandText of this command; can be null if unavailable.
271: */
272: public String getCommandText() {
273:
274: return commandText;
275: }
276:
277: /**
278: * Sets the raw command text used for this command.
279: * <p>
280: * This is typically either the raw text of the command like the SQL statement executed. This field may not be
281: * available for all types of commands where the command cannot be effectively reproduced. Such as when a
282: * transaction was started by setting the auto-commit state to false on the connection.
283: *
284: * @param commandText of the command being executed; can be null if no text command was executed.
285: */
286: public void setCommandText(String content) {
287:
288: this .commandText = content;
289: }
290:
291: /**
292: * Sets the name of the iSQL-Viewer service this historical command is associated with.
293: * <p>
294: *
295: * @param service name to set for this event.
296: * @throws NullPointerException if the given service is null.
297: */
298: public void setService(String service) {
299:
300: Assertions.assertNotNull("historical-command-service", service);
301: this .service = service;
302: }
303:
304: /**
305: * Sets the name of the iSQL-Viewer service this historical command is associated with.
306: * <p>
307: *
308: * @return the service that this event was executed against; can be null if not previously set.
309: */
310: public String getService() {
311:
312: return service;
313: }
314:
315: /**
316: * Add sub-command to this command context.
317: * <p>
318: * Adds a child command that effectively puts it as a child process of sorts. Not all command types can have
319: * subCommands however, and command types may only allow specific child types of commands.
320: *
321: * @param subcommand to add to this event.
322: * @return <tt>true</tt> if the subcommand was successfully added to this event.
323: * @throws NullPointerException if the subcommand is null.
324: * @throws IllegalArgumentException if the subcommand transaction is not equal to this instances' transaction id.
325: */
326: public boolean addSubcommand(HistoricalCommand subcommand) {
327:
328: long txnID = getTransactionId();
329: Assertions.assertNotNull("historical-command-subcommand",
330: subcommand);
331: Assertions.assertEqual("history-subcommand-transaction-id",
332: txnID, subcommand.getTransactionId());
333: return subCommands.add(subcommand);
334: }
335:
336: /**
337: * Acquires enumeration of child process within the command context.
338: * <p>
339: *
340: * @return enumerated child commands that were executed as a part of this instance.
341: */
342: public Enumeration<HistoricalCommand> elements() {
343:
344: return subCommands.elements();
345: }
346:
347: /**
348: * Gets child command by index.
349: * <p>
350: *
351: * @param index of the desired child command.
352: * @return the command at the given index.
353: * @throws ArrayIndexOutOfBoundsException if index is invalid.
354: */
355: public HistoricalCommand getChildCommand(int index) {
356:
357: return subCommands.get(index);
358: }
359:
360: /**
361: * Get the index of the child command in this command context.
362: * <p>
363: *
364: * @param childCommand to seek the index of within this instance.
365: * @return index of the command in this instance; -1 if not found.
366: */
367: public int indexOf(HistoricalCommand childCommand) {
368:
369: return subCommands.indexOf(childCommand);
370: }
371:
372: /**
373: * Gets the number of child commands that were part of this instance.
374: * <p>
375: *
376: * @return number of child processes that were executed in this command context.
377: */
378: public int size() {
379:
380: return subCommands.size();
381: }
382:
383: /**
384: * Determines if this historical command has child commands.
385: * <p>
386: *
387: * @return <tt>true</tt> if this command instance contains child processes.
388: */
389: public boolean hasChildCommands() {
390:
391: return !subCommands.isEmpty();
392: }
393:
394: public int getChildCount() {
395:
396: return subCommands.size();
397: }
398: }
|