package de.masters_of_disaster.ant.tasks.ar; import java.io.File; import java.util.Date; /** * This class represents an entry in an Ar archive. It consists * of the entry's header, as well as the entry's File. Entries * can be instantiated in one of three ways, depending on how * they are to be used. * <p> * ArEntries that are created from the header bytes read from * an archive are instantiated with the ArEntry( byte[] ) * constructor. These entries will be used when extracting from * or listing the contents of an archive. These entries have their * header filled in using the header bytes. They also set the File * to null, since they reference an archive entry not a file. * <p> * ArEntries that are created from Files that are to be written * into an archive are instantiated with the ArEntry( File ) * constructor. These entries have their header filled in using * the File's information. They also keep a reference to the File * for convenience when writing entries. * <p> * Finally, ArEntries can be constructed from nothing but a name. * This allows the programmer to construct the entry by hand, for * instance when only an InputStream is available for writing to * the archive, and the header information is constructed from * other information. In this case the header fields are set to * defaults and the File is set to null. * * <p> * The C structure for an Ar Entry's header is: * <pre> * struct header { * char filename[16]; * char filedate[12]; * char uid[6]; * char gid[6]; * char mode[8]; * char size[10]; * char magic[2]; * } header; * </pre> * */ public class ArEntry implements ArConstants { /** The entry's filename. */ private StringBuffer filename; /** The entry's file date. */ private long fileDate; /** The entry's user id. */ private int userId; /** The entry's group id. */ private int groupId; /** The entry's permission mode. */ private int mode; /** The entry's size. */ private long size; /** The entry's magic tag. */ private StringBuffer magic; /** The entry's file reference */ private File file; /** Default permissions bits for files */ public static final int DEFAULT_FILE_MODE = 0100644; /** Convert millis to seconds */ public static final int MILLIS_PER_SECOND = 1000; /** * Construct an empty entry and prepares the header values. */ private ArEntry () { this.magic = new StringBuffer(HEADERMAGIC); this.filename = new StringBuffer(); this.userId = 0; this.groupId = 0; this.file = null; } /** * Construct an entry with only a name. This allows the programmer * to construct the entry's header "by hand". File is set to null. * * @param name the entry name */ public ArEntry(String name) { this(); if (name.endsWith("/")) { throw new IllegalArgumentException("ar archives can only contain files"); } this.filename = new StringBuffer(name); this.mode = DEFAULT_FILE_MODE; this.userId = 0; this.groupId = 0; this.size = 0; this.fileDate = (new Date()).getTime() / MILLIS_PER_SECOND; } /** * Construct an entry for a file. File is set to file, and the * header is constructed from information from the file. * * @param file The file that the entry represents. */ public ArEntry(File file) { this(); if (file.isDirectory()) { throw new IllegalArgumentException("ar archives can only contain files"); } this.file = file; this.filename = new StringBuffer(file.getName()); this.fileDate = file.lastModified() / MILLIS_PER_SECOND; this.mode = DEFAULT_FILE_MODE; this.size = file.length(); } /** * Construct an entry from an archive's header bytes. File is set * to null. * * @param headerBuf The header bytes from an ar archive entry. */ public ArEntry(byte[] headerBuf) { this(); this.parseArHeader(headerBuf); } /** * Determine if the two entries are equal. Equality is determined * by the header names being equal. * * @param it Entry to be checked for equality. * @return True if the entries are equal. */ public boolean equals(ArEntry it) { return this.getFilename().equals(it.getFilename()); } /** * Determine if the two entries are equal. Equality is determined * by the header names being equal. * * @param it Entry to be checked for equality. * @return True if the entries are equal. */ public boolean equals(Object it) { if (it == null || getClass() != it.getClass()) { return false; } return equals((ArEntry) it); } /** * Hashcodes are based on entry names. * * @return the entry hashcode */ public int hashCode() { return getFilename().hashCode(); } /** * Get this entry's name. * * @return This entry's name. */ public String getFilename() { return this.filename.toString(); } /** * Set this entry's name. * * @param name This entry's new name. */ public void setFilename(String filename) { this.filename = new StringBuffer(filename); } /** * Set the mode for this entry * * @param mode the mode for this entry */ public void setMode(int mode) { this.mode = mode; } /** * Get this entry's user id. * * @return This entry's user id. */ public int getUserId() { return this.userId; } /** * Set this entry's user id. * * @param userId This entry's new user id. */ public void setUserId(int userId) { this.userId = userId; } /** * Get this entry's group id. * * @return This entry's group id. */ public int getGroupId() { return this.groupId; } /** * Set this entry's group id. * * @param groupId This entry's new group id. */ public void setGroupId(int groupId) { this.groupId = groupId; } /** * Convenience method to set this entry's group and user ids. * * @param userId This entry's new user id. * @param groupId This entry's new group id. */ public void setIds(int userId, int groupId) { this.setUserId(userId); this.setGroupId(groupId); } /** * Set this entry's modification time. The parameter passed * to this method is in "Java time". * * @param time This entry's new modification time. */ public void setFileDate(long time) { this.fileDate = time / MILLIS_PER_SECOND; } /** * Set this entry's modification time. * * @param time This entry's new modification time. */ public void setFileDate(Date time) { this.fileDate = time.getTime() / MILLIS_PER_SECOND; } /** * Get this entry's modification time. * * @return time This entry's new modification time. */ public Date getFileDate() { return new Date(this.fileDate * MILLIS_PER_SECOND); } /** * Get this entry's file. * * @return This entry's file. */ public File getFile() { return this.file; } /** * Get this entry's mode. * * @return This entry's mode. */ public int getMode() { return this.mode; } /** * Get this entry's file size. * * @return This entry's file size. */ public long getSize() { return this.size; } /** * Set this entry's file size. * * @param size This entry's new file size. */ public void setSize(long size) { this.size = size; } /** * Write an entry's header information to a header buffer. * * @param outbuf The tar entry header buffer to fill in. */ public void writeEntryHeader(byte[] outbuf) { int offset = 0; offset = ArUtils.getNameBytes(this.filename, outbuf, offset, NAMELEN); offset = ArUtils.getLongBytes(this.fileDate, outbuf, offset, FILEDATELEN); offset = ArUtils.getIntegerBytes(this.userId, outbuf, offset, UIDLEN); offset = ArUtils.getIntegerBytes(this.groupId, outbuf, offset, GIDLEN); offset = ArUtils.getOctalBytes(this.mode, outbuf, offset, MODELEN); offset = ArUtils.getLongBytes(this.size, outbuf, offset, SIZELEN); offset = ArUtils.getNameBytes(this.magic, outbuf, offset, MAGICLEN); while (offset < outbuf.length) { outbuf[offset++] = 0; } } /** * Parse an entry's header information from a header buffer. * * @param header The ar entry header buffer to get information from. */ public void parseArHeader(byte[] header) { throw new UnsupportedOperationException("parseArHeader(byte[]) not yet implmented"); // int offset = 0; // // this.filename = TarUtils.parseName(header, offset, NAMELEN); // offset += NAMELEN; // this.fileDate = TarUtils.parseOctal(header, offset, FILEDATELEN); // offset += FILEDATELEN; // this.userId = (int) TarUtils.parseOctal(header, offset, UIDLEN); // offset += UIDLEN; // this.groupId = (int) TarUtils.parseOctal(header, offset, GIDLEN); // offset += GIDLEN; // this.mode = (int) TarUtils.parseOctal(header, offset, MODELEN); // offset += MODELEN; // this.size = TarUtils.parseOctal(header, offset, SIZELEN); // offset += SIZELEN; // this.magic = TarUtils.parseName(header, offset, MAGICLEN); // offset += MAGICLEN; } }