001: package de.masters_of_disaster.ant.tasks.ar;
002:
003: import java.io.File;
004: import java.util.Date;
005:
006: /**
007: * This class represents an entry in an Ar archive. It consists
008: * of the entry's header, as well as the entry's File. Entries
009: * can be instantiated in one of three ways, depending on how
010: * they are to be used.
011: * <p>
012: * ArEntries that are created from the header bytes read from
013: * an archive are instantiated with the ArEntry( byte[] )
014: * constructor. These entries will be used when extracting from
015: * or listing the contents of an archive. These entries have their
016: * header filled in using the header bytes. They also set the File
017: * to null, since they reference an archive entry not a file.
018: * <p>
019: * ArEntries that are created from Files that are to be written
020: * into an archive are instantiated with the ArEntry( File )
021: * constructor. These entries have their header filled in using
022: * the File's information. They also keep a reference to the File
023: * for convenience when writing entries.
024: * <p>
025: * Finally, ArEntries can be constructed from nothing but a name.
026: * This allows the programmer to construct the entry by hand, for
027: * instance when only an InputStream is available for writing to
028: * the archive, and the header information is constructed from
029: * other information. In this case the header fields are set to
030: * defaults and the File is set to null.
031: *
032: * <p>
033: * The C structure for an Ar Entry's header is:
034: * <pre>
035: * struct header {
036: * char filename[16];
037: * char filedate[12];
038: * char uid[6];
039: * char gid[6];
040: * char mode[8];
041: * char size[10];
042: * char magic[2];
043: * } header;
044: * </pre>
045: *
046: */
047:
048: public class ArEntry implements ArConstants {
049: /** The entry's filename. */
050: private StringBuffer filename;
051:
052: /** The entry's file date. */
053: private long fileDate;
054:
055: /** The entry's user id. */
056: private int userId;
057:
058: /** The entry's group id. */
059: private int groupId;
060:
061: /** The entry's permission mode. */
062: private int mode;
063:
064: /** The entry's size. */
065: private long size;
066:
067: /** The entry's magic tag. */
068: private StringBuffer magic;
069:
070: /** The entry's file reference */
071: private File file;
072:
073: /** Default permissions bits for files */
074: public static final int DEFAULT_FILE_MODE = 0100644;
075:
076: /** Convert millis to seconds */
077: public static final int MILLIS_PER_SECOND = 1000;
078:
079: /**
080: * Construct an empty entry and prepares the header values.
081: */
082: private ArEntry() {
083: this .magic = new StringBuffer(HEADERMAGIC);
084: this .filename = new StringBuffer();
085: this .userId = 0;
086: this .groupId = 0;
087: this .file = null;
088: }
089:
090: /**
091: * Construct an entry with only a name. This allows the programmer
092: * to construct the entry's header "by hand". File is set to null.
093: *
094: * @param name the entry name
095: */
096: public ArEntry(String name) {
097: this ();
098: if (name.endsWith("/")) {
099: throw new IllegalArgumentException(
100: "ar archives can only contain files");
101: }
102: this .filename = new StringBuffer(name);
103: this .mode = DEFAULT_FILE_MODE;
104: this .userId = 0;
105: this .groupId = 0;
106: this .size = 0;
107: this .fileDate = (new Date()).getTime() / MILLIS_PER_SECOND;
108: }
109:
110: /**
111: * Construct an entry for a file. File is set to file, and the
112: * header is constructed from information from the file.
113: *
114: * @param file The file that the entry represents.
115: */
116: public ArEntry(File file) {
117: this ();
118: if (file.isDirectory()) {
119: throw new IllegalArgumentException(
120: "ar archives can only contain files");
121: }
122: this .file = file;
123: this .filename = new StringBuffer(file.getName());
124: this .fileDate = file.lastModified() / MILLIS_PER_SECOND;
125: this .mode = DEFAULT_FILE_MODE;
126: this .size = file.length();
127: }
128:
129: /**
130: * Construct an entry from an archive's header bytes. File is set
131: * to null.
132: *
133: * @param headerBuf The header bytes from an ar archive entry.
134: */
135: public ArEntry(byte[] headerBuf) {
136: this ();
137: this .parseArHeader(headerBuf);
138: }
139:
140: /**
141: * Determine if the two entries are equal. Equality is determined
142: * by the header names being equal.
143: *
144: * @param it Entry to be checked for equality.
145: * @return True if the entries are equal.
146: */
147: public boolean equals(ArEntry it) {
148: return this .getFilename().equals(it.getFilename());
149: }
150:
151: /**
152: * Determine if the two entries are equal. Equality is determined
153: * by the header names being equal.
154: *
155: * @param it Entry to be checked for equality.
156: * @return True if the entries are equal.
157: */
158: public boolean equals(Object it) {
159: if (it == null || getClass() != it.getClass()) {
160: return false;
161: }
162: return equals((ArEntry) it);
163: }
164:
165: /**
166: * Hashcodes are based on entry names.
167: *
168: * @return the entry hashcode
169: */
170: public int hashCode() {
171: return getFilename().hashCode();
172: }
173:
174: /**
175: * Get this entry's name.
176: *
177: * @return This entry's name.
178: */
179: public String getFilename() {
180: return this .filename.toString();
181: }
182:
183: /**
184: * Set this entry's name.
185: *
186: * @param name This entry's new name.
187: */
188: public void setFilename(String filename) {
189: this .filename = new StringBuffer(filename);
190: }
191:
192: /**
193: * Set the mode for this entry
194: *
195: * @param mode the mode for this entry
196: */
197: public void setMode(int mode) {
198: this .mode = mode;
199: }
200:
201: /**
202: * Get this entry's user id.
203: *
204: * @return This entry's user id.
205: */
206: public int getUserId() {
207: return this .userId;
208: }
209:
210: /**
211: * Set this entry's user id.
212: *
213: * @param userId This entry's new user id.
214: */
215: public void setUserId(int userId) {
216: this .userId = userId;
217: }
218:
219: /**
220: * Get this entry's group id.
221: *
222: * @return This entry's group id.
223: */
224: public int getGroupId() {
225: return this .groupId;
226: }
227:
228: /**
229: * Set this entry's group id.
230: *
231: * @param groupId This entry's new group id.
232: */
233: public void setGroupId(int groupId) {
234: this .groupId = groupId;
235: }
236:
237: /**
238: * Convenience method to set this entry's group and user ids.
239: *
240: * @param userId This entry's new user id.
241: * @param groupId This entry's new group id.
242: */
243: public void setIds(int userId, int groupId) {
244: this .setUserId(userId);
245: this .setGroupId(groupId);
246: }
247:
248: /**
249: * Set this entry's modification time. The parameter passed
250: * to this method is in "Java time".
251: *
252: * @param time This entry's new modification time.
253: */
254: public void setFileDate(long time) {
255: this .fileDate = time / MILLIS_PER_SECOND;
256: }
257:
258: /**
259: * Set this entry's modification time.
260: *
261: * @param time This entry's new modification time.
262: */
263: public void setFileDate(Date time) {
264: this .fileDate = time.getTime() / MILLIS_PER_SECOND;
265: }
266:
267: /**
268: * Get this entry's modification time.
269: *
270: * @return time This entry's new modification time.
271: */
272: public Date getFileDate() {
273: return new Date(this .fileDate * MILLIS_PER_SECOND);
274: }
275:
276: /**
277: * Get this entry's file.
278: *
279: * @return This entry's file.
280: */
281: public File getFile() {
282: return this .file;
283: }
284:
285: /**
286: * Get this entry's mode.
287: *
288: * @return This entry's mode.
289: */
290: public int getMode() {
291: return this .mode;
292: }
293:
294: /**
295: * Get this entry's file size.
296: *
297: * @return This entry's file size.
298: */
299: public long getSize() {
300: return this .size;
301: }
302:
303: /**
304: * Set this entry's file size.
305: *
306: * @param size This entry's new file size.
307: */
308: public void setSize(long size) {
309: this .size = size;
310: }
311:
312: /**
313: * Write an entry's header information to a header buffer.
314: *
315: * @param outbuf The tar entry header buffer to fill in.
316: */
317: public void writeEntryHeader(byte[] outbuf) {
318: int offset = 0;
319:
320: offset = ArUtils.getNameBytes(this .filename, outbuf, offset,
321: NAMELEN);
322: offset = ArUtils.getLongBytes(this .fileDate, outbuf, offset,
323: FILEDATELEN);
324: offset = ArUtils.getIntegerBytes(this .userId, outbuf, offset,
325: UIDLEN);
326: offset = ArUtils.getIntegerBytes(this .groupId, outbuf, offset,
327: GIDLEN);
328: offset = ArUtils.getOctalBytes(this .mode, outbuf, offset,
329: MODELEN);
330: offset = ArUtils.getLongBytes(this .size, outbuf, offset,
331: SIZELEN);
332: offset = ArUtils.getNameBytes(this .magic, outbuf, offset,
333: MAGICLEN);
334:
335: while (offset < outbuf.length) {
336: outbuf[offset++] = 0;
337: }
338: }
339:
340: /**
341: * Parse an entry's header information from a header buffer.
342: *
343: * @param header The ar entry header buffer to get information from.
344: */
345: public void parseArHeader(byte[] header) {
346: throw new UnsupportedOperationException(
347: "parseArHeader(byte[]) not yet implmented");
348: // int offset = 0;
349: //
350: // this.filename = TarUtils.parseName(header, offset, NAMELEN);
351: // offset += NAMELEN;
352: // this.fileDate = TarUtils.parseOctal(header, offset, FILEDATELEN);
353: // offset += FILEDATELEN;
354: // this.userId = (int) TarUtils.parseOctal(header, offset, UIDLEN);
355: // offset += UIDLEN;
356: // this.groupId = (int) TarUtils.parseOctal(header, offset, GIDLEN);
357: // offset += GIDLEN;
358: // this.mode = (int) TarUtils.parseOctal(header, offset, MODELEN);
359: // offset += MODELEN;
360: // this.size = TarUtils.parseOctal(header, offset, SIZELEN);
361: // offset += SIZELEN;
362: // this.magic = TarUtils.parseName(header, offset, MAGICLEN);
363: // offset += MAGICLEN;
364: }
365: }
|