tikhomirov@10: /* tikhomirov@74: * Copyright (c) 2010-2011 TMate Software Ltd tikhomirov@74: * tikhomirov@74: * This program is free software; you can redistribute it and/or modify tikhomirov@74: * it under the terms of the GNU General Public License as published by tikhomirov@74: * the Free Software Foundation; version 2 of the License. tikhomirov@74: * tikhomirov@74: * This program is distributed in the hope that it will be useful, tikhomirov@74: * but WITHOUT ANY WARRANTY; without even the implied warranty of tikhomirov@74: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the tikhomirov@74: * GNU General Public License for more details. tikhomirov@74: * tikhomirov@74: * For information on how to redistribute this software under tikhomirov@74: * the terms of a license other than GNU General Public License tikhomirov@74: * contact TMate Software at support@svnkit.com tikhomirov@10: */ tikhomirov@74: package org.tmatesoft.hg.repo; tikhomirov@10: tikhomirov@10: import java.io.File; tikhomirov@10: import java.io.IOException; tikhomirov@10: import java.util.Collections; tikhomirov@18: import java.util.LinkedHashMap; tikhomirov@18: import java.util.Map; tikhomirov@18: import java.util.TreeSet; tikhomirov@10: tikhomirov@74: import org.tmatesoft.hg.internal.DataAccess; tikhomirov@74: import org.tmatesoft.hg.internal.DataAccessProvider; tikhomirov@74: tikhomirov@10: tikhomirov@10: /** tikhomirov@10: * @see http://mercurial.selenic.com/wiki/DirState tikhomirov@10: * @see http://mercurial.selenic.com/wiki/FileFormats#dirstate tikhomirov@74: * tikhomirov@74: * @author Artem Tikhomirov tikhomirov@74: * @author TMate Software Ltd. tikhomirov@10: */ tikhomirov@93: class HgDirstate { tikhomirov@10: tikhomirov@59: private final DataAccessProvider accessProvider; tikhomirov@10: private final File dirstateFile; tikhomirov@18: private Map normal; tikhomirov@18: private Map added; tikhomirov@18: private Map removed; tikhomirov@18: private Map merged; tikhomirov@10: tikhomirov@59: /*package-local*/ HgDirstate() { tikhomirov@59: // empty instance tikhomirov@59: accessProvider = null; tikhomirov@59: dirstateFile = null; tikhomirov@59: } tikhomirov@59: tikhomirov@59: public HgDirstate(DataAccessProvider dap, File dirstate) { tikhomirov@59: accessProvider = dap; tikhomirov@59: dirstateFile = dirstate; tikhomirov@10: } tikhomirov@10: tikhomirov@10: private void read() { tikhomirov@18: normal = added = removed = merged = Collections.emptyMap(); tikhomirov@59: if (dirstateFile == null || !dirstateFile.exists()) { tikhomirov@10: return; tikhomirov@10: } tikhomirov@59: DataAccess da = accessProvider.create(dirstateFile); tikhomirov@10: if (da.isEmpty()) { tikhomirov@10: return; tikhomirov@10: } tikhomirov@18: // not sure linked is really needed here, just for ease of debug tikhomirov@18: normal = new LinkedHashMap(); tikhomirov@18: added = new LinkedHashMap(); tikhomirov@18: removed = new LinkedHashMap(); tikhomirov@18: merged = new LinkedHashMap(); tikhomirov@10: try { tikhomirov@10: // XXX skip(40) if we don't need these? tikhomirov@10: byte[] parents = new byte[40]; tikhomirov@10: da.readBytes(parents, 0, 40); tikhomirov@10: parents = null; tikhomirov@10: do { tikhomirov@10: final byte state = da.readByte(); tikhomirov@10: final int fmode = da.readInt(); tikhomirov@10: final int size = da.readInt(); tikhomirov@10: final int time = da.readInt(); tikhomirov@10: final int nameLen = da.readInt(); tikhomirov@10: String fn1 = null, fn2 = null; tikhomirov@10: byte[] name = new byte[nameLen]; tikhomirov@10: da.readBytes(name, 0, nameLen); tikhomirov@10: for (int i = 0; i < nameLen; i++) { tikhomirov@10: if (name[i] == 0) { tikhomirov@11: fn1 = new String(name, 0, i, "UTF-8"); // XXX unclear from documentation what encoding is used there tikhomirov@11: fn2 = new String(name, i+1, nameLen - i - 1, "UTF-8"); // need to check with different system codepages tikhomirov@10: break; tikhomirov@10: } tikhomirov@10: } tikhomirov@10: if (fn1 == null) { tikhomirov@10: fn1 = new String(name); tikhomirov@10: } tikhomirov@10: Record r = new Record(fmode, size, time, fn1, fn2); tikhomirov@10: if (state == 'n') { tikhomirov@18: normal.put(r.name1, r); tikhomirov@10: } else if (state == 'a') { tikhomirov@18: added.put(r.name1, r); tikhomirov@10: } else if (state == 'r') { tikhomirov@18: removed.put(r.name1, r); tikhomirov@10: } else if (state == 'm') { tikhomirov@18: merged.put(r.name1, r); tikhomirov@10: } else { tikhomirov@10: // FIXME log error? tikhomirov@10: } tikhomirov@10: } while (!da.isEmpty()); tikhomirov@10: } catch (IOException ex) { tikhomirov@10: ex.printStackTrace(); // FIXME log error, clean dirstate? tikhomirov@10: } finally { tikhomirov@10: da.done(); tikhomirov@10: } tikhomirov@10: } tikhomirov@10: tikhomirov@18: // new, modifiable collection tikhomirov@18: /*package-local*/ TreeSet all() { tikhomirov@18: read(); tikhomirov@18: TreeSet rv = new TreeSet(); tikhomirov@18: @SuppressWarnings("unchecked") tikhomirov@18: Map[] all = new Map[] { normal, added, removed, merged }; tikhomirov@18: for (int i = 0; i < all.length; i++) { tikhomirov@18: for (Record r : all[i].values()) { tikhomirov@18: rv.add(r.name1); tikhomirov@18: } tikhomirov@18: } tikhomirov@18: return rv; tikhomirov@18: } tikhomirov@18: tikhomirov@18: /*package-local*/ Record checkNormal(String fname) { tikhomirov@18: return normal.get(fname); tikhomirov@18: } tikhomirov@18: tikhomirov@18: /*package-local*/ Record checkAdded(String fname) { tikhomirov@18: return added.get(fname); tikhomirov@18: } tikhomirov@18: /*package-local*/ Record checkRemoved(String fname) { tikhomirov@18: return removed.get(fname); tikhomirov@18: } tikhomirov@18: /*package-local*/ Record checkMerged(String fname) { tikhomirov@18: return merged.get(fname); tikhomirov@18: } tikhomirov@18: tikhomirov@18: tikhomirov@18: tikhomirov@18: tikhomirov@93: /*package-local*/ void dump() { tikhomirov@10: read(); tikhomirov@10: @SuppressWarnings("unchecked") tikhomirov@18: Map[] all = new Map[] { normal, added, removed, merged }; tikhomirov@10: char[] x = new char[] {'n', 'a', 'r', 'm' }; tikhomirov@10: for (int i = 0; i < all.length; i++) { tikhomirov@18: for (Record r : all[i].values()) { tikhomirov@14: System.out.printf("%c %3o%6d %30tc\t\t%s", x[i], r.mode, r.size, (long) r.time * 1000, r.name1); tikhomirov@10: if (r.name2 != null) { tikhomirov@10: System.out.printf(" --> %s", r.name2); tikhomirov@10: } tikhomirov@10: System.out.println(); tikhomirov@10: } tikhomirov@10: System.out.println(); tikhomirov@10: } tikhomirov@10: } tikhomirov@10: tikhomirov@18: /*package-local*/ static class Record { tikhomirov@10: final int mode; tikhomirov@10: final int size; tikhomirov@10: final int time; tikhomirov@10: final String name1; tikhomirov@10: final String name2; tikhomirov@10: tikhomirov@10: public Record(int fmode, int fsize, int ftime, String name1, String name2) { tikhomirov@10: mode = fmode; tikhomirov@10: size = fsize; tikhomirov@10: time = ftime; tikhomirov@10: this.name1 = name1; tikhomirov@10: this.name2 = name2; tikhomirov@10: tikhomirov@10: } tikhomirov@10: } tikhomirov@10: }