/******************************************************************************* * Copyright (c) 2004, 2006 * Thomas Hallgren, Kenneth Olwing, Mitch Sonies * Pontus Rydin, Nils Unden, Peer Torngren * The code, documentation and other materials contained herein have been * licensed under the Eclipse Public License - v 1.0 by the individual * copyright holders listed above, as Initial Contributors under such license. * The text of such license is available at www.eclipse.org. *******************************************************************************/ package org.eclipse.buckminster.manifest; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.eclipse.core.runtime.IProgressMonitor; public class PathEntry implements Comparable<PathEntry> { private static final String HEADER = "PATHENTRY:v0"; private static final String NAME_PREFIX = "NAME:"; private static final String CHECKSUMS_PREFIX = "CHECKSUMS:"; public static final PathEntry[] EMPTY_LIST = new PathEntry[0]; private final String m_name; private Checksum[] m_checksums = Checksum.EMPTY_LIST; public static PathEntry fromBufferedReader(BufferedReader br) throws IOException, MissingDataException, ChecksumMismatchException { String tmp; if (!br.readLine().equals(HEADER)) throw new MissingDataException("Missing header in persisted PathEntry"); tmp = br.readLine(); if (!tmp.startsWith(NAME_PREFIX)) throw new MissingDataException("Missing name in persisted PathEntry"); String name = tmp.substring(NAME_PREFIX.length()); tmp = br.readLine(); if (!tmp.startsWith(CHECKSUMS_PREFIX)) throw new MissingDataException("Missing checksums in persisted PathEntry"); Checksum[] checksums = new Checksum[Integer.parseInt(tmp.substring(CHECKSUMS_PREFIX.length()))]; for (int i = 0; i < checksums.length; i++) checksums[i] = Checksum.fromBufferedReader(br); return new PathEntry(name, checksums); } public PathEntry(File path, File root, IProgressMonitor monitor) throws ChecksumMismatchException, PathMismatchException, NoSuchAlgorithmException, IOException { this(path, root, null, null, monitor); } public PathEntry(File path, File root, String algorithm, String assumedLineSeparator, IProgressMonitor monitor) throws ChecksumMismatchException, PathMismatchException, NoSuchAlgorithmException, IOException { this(path, root, new Checksum[] { new Checksum(path, assumedLineSeparator, algorithm, monitor) }); } public PathEntry(File path, File root, Checksum[] checksums) throws ChecksumMismatchException, PathMismatchException { this(canonicalize(path, root), checksums); } /* package */PathEntry(String name, Checksum[] checksums) throws ChecksumMismatchException { m_name = this.canonicalizeSeparator(name); this.addChecksums(checksums); } /* package */void addChecksums(Checksum[] checksums) throws ChecksumMismatchException { // only add checksums if we don't have them // if an attempt is made to add a checksum we do have, ensure the // existing one really is equal to existing // List<Checksum> checksums2Add = new ArrayList<Checksum>(); for (Checksum c2add : checksums) { Checksum existing = this.internalGetChecksum(c2add.getAlgorithm(), c2add.getAssumedLineSeparator(), checksums2Add); if (existing == null) checksums2Add.add(c2add); else if (!c2add.equals(existing)) throw new ChecksumMismatchException(existing, c2add); } int sz = checksums2Add.size(); if (sz > 0) { Checksum[] newChecksums = new Checksum[m_checksums.length + sz]; System.arraycopy(m_checksums, 0, newChecksums, 0, m_checksums.length); System.arraycopy(checksums2Add.toArray(Checksum.EMPTY_LIST), 0, newChecksums, m_checksums.length, sz); m_checksums = newChecksums; Arrays.sort(m_checksums); } } public String getName() { return m_name; } public boolean isDirectory() { return m_name.endsWith(Constants.CANONICAL_SEPARATOR); } public Checksum getChecksum() { return this.getChecksum(null, null); } public Checksum getChecksum(String algorithm, String assumedLineSeparator) { return this.internalGetChecksum(algorithm, assumedLineSeparator, null); } public Checksum[] getAllChecksums() { return m_checksums; } public boolean matches(PathEntry that) { if (this.equals(that)) return true; if (!this.m_name.equals(that.m_name)) return false; for (Checksum thisChecksum : this.getAllChecksums()) { Checksum thatChecksum = that.getChecksum(thisChecksum.getAlgorithm(), thisChecksum .getAssumedLineSeparator()); if (thatChecksum != null && !thisChecksum.equals(thatChecksum)) return false; } for (Checksum thatChecksum : that.getAllChecksums()) { Checksum thisChecksum = this.getChecksum(thatChecksum.getAlgorithm(), thatChecksum .getAssumedLineSeparator()); if (thisChecksum != null && !thatChecksum.equals(thisChecksum)) return false; } return true; } public int compareTo(PathEntry that) { if (that == this) return 0; int v = this.m_name.compareTo(that.m_name); if (v != 0) return v; if (Arrays.equals(this.m_checksums, that.m_checksums)) return 0; int i = 0; while (i < this.m_checksums.length) { if (that.m_checksums.length <= i) return 1; v = this.m_checksums[i].compareTo(that.m_checksums[i]); if (v != 0) return v; i++; } if (that.m_checksums.length >= i) return -1; throw new InternalError(); } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof PathEntry)) return false; PathEntry that = (PathEntry)o; if (!this.m_name.equals(that.m_name)) return false; if (!Arrays.equals(this.m_checksums, that.m_checksums)) return false; return true; } @Override public int hashCode() { int result = 17; result = 37 * result + this.m_name.hashCode(); result = 37 * result + Arrays.hashCode(this.m_checksums); return result; } @Override public String toString() { StringBuilder sb = new StringBuilder(m_name); sb.append(Arrays.toString(this.getAllChecksums())); return sb.toString(); } public void toPrintWriter(PrintWriter pw) { pw.println(HEADER); pw.print(NAME_PREFIX); pw.println(m_name); pw.print(CHECKSUMS_PREFIX); pw.println(m_checksums.length); for (Checksum c : m_checksums) c.toPrintWriter(pw); } private static String canonicalize(File path, File root) throws PathMismatchException { String strPath = path.getPath(); String rootPath = root.getPath(); if (!strPath.startsWith(rootPath)) throw new PathMismatchException(rootPath, strPath); StringBuilder sb = new StringBuilder(strPath.substring(rootPath.length() + 1)); if (path.isDirectory()) sb.append(File.separatorChar); return sb.toString(); } private String canonicalizeSeparator(String name) { return name.replace(File.separatorChar, Constants.CANONICAL_SEPARATOR_CHAR); } private Checksum internalGetChecksum(String algorithm, String assumedLineSeparator, List<Checksum> additionalList) { for (Checksum c : m_checksums) if ((algorithm == null || c.getAlgorithm().equals(algorithm)) && (assumedLineSeparator == null || c.getAssumedLineSeparator().equals(assumedLineSeparator))) return c; if (additionalList != null) for (Checksum c : additionalList) if ((algorithm == null || c.getAlgorithm().equals(algorithm)) && (assumedLineSeparator == null || c.getAssumedLineSeparator().equals(assumedLineSeparator))) return c; return null; } }