kitaev@213: /* kitaev@213: * Copyright (c) 2011 TMate Software Ltd kitaev@213: * kitaev@213: * This program is free software; you can redistribute it and/or modify kitaev@213: * it under the terms of the GNU General Public License as published by kitaev@213: * the Free Software Foundation; version 2 of the License. kitaev@213: * kitaev@213: * This program is distributed in the hope that it will be useful, kitaev@213: * but WITHOUT ANY WARRANTY; without even the implied warranty of kitaev@213: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the kitaev@213: * GNU General Public License for more details. kitaev@213: * kitaev@213: * For information on how to redistribute this software under kitaev@213: * the terms of a license other than GNU General Public License kitaev@213: * contact TMate Software at support@hg4j.com kitaev@213: */ kitaev@213: package org.tmatesoft.hg.repo; kitaev@213: kitaev@213: import java.io.BufferedReader; kitaev@213: import java.io.File; kitaev@213: import java.io.FileReader; kitaev@213: import java.io.IOException; kitaev@213: import java.util.ArrayList; kitaev@213: import java.util.HashMap; kitaev@213: import java.util.LinkedList; kitaev@213: import java.util.List; kitaev@213: import java.util.Map; kitaev@213: import java.util.TreeMap; kitaev@213: kitaev@213: import org.tmatesoft.hg.core.Nodeid; kitaev@213: kitaev@213: /** kitaev@213: * @see http://mercurial.selenic.com/wiki/TagDesign kitaev@213: * kitaev@213: * @author Artem Tikhomirov kitaev@213: * @author TMate Software Ltd. kitaev@213: */ kitaev@213: public class HgTags { kitaev@213: // global tags come from ".hgtags" kitaev@213: // local come from ".hg/localtags" kitaev@213: kitaev@213: private final Map> globalToName; kitaev@213: private final Map> localToName; kitaev@213: private final Map> globalFromName; kitaev@213: private final Map> localFromName; kitaev@213: kitaev@213: kitaev@213: /*package-local*/ HgTags() { kitaev@213: globalToName = new HashMap>(); kitaev@213: localToName = new HashMap>(); kitaev@213: globalFromName = new TreeMap>(); kitaev@213: localFromName = new TreeMap>(); kitaev@213: } kitaev@213: kitaev@213: /*package-local*/ void readLocal(File localTags) throws IOException { kitaev@213: if (localTags == null || localTags.isDirectory()) { kitaev@213: throw new IllegalArgumentException(String.valueOf(localTags)); kitaev@213: } kitaev@213: read(localTags, localToName, localFromName); kitaev@213: } kitaev@213: kitaev@213: /*package-local*/ void readGlobal(File globalTags) throws IOException { kitaev@213: if (globalTags == null || globalTags.isDirectory()) { kitaev@213: throw new IllegalArgumentException(String.valueOf(globalTags)); kitaev@213: } kitaev@213: read(globalTags, globalToName, globalFromName); kitaev@213: } kitaev@213: kitaev@213: private void read(File f, Map> nid2name, Map> name2nid) throws IOException { kitaev@213: if (!f.canRead()) { kitaev@213: return; kitaev@213: } kitaev@213: BufferedReader r = null; kitaev@213: try { kitaev@213: r = new BufferedReader(new FileReader(f)); kitaev@213: read(r, nid2name, name2nid); kitaev@213: } finally { kitaev@213: if (r != null) { kitaev@213: r.close(); kitaev@213: } kitaev@213: } kitaev@213: } kitaev@213: kitaev@213: private void read(BufferedReader reader, Map> nid2name, Map> name2nid) throws IOException { kitaev@213: String line; kitaev@213: while ((line = reader.readLine()) != null) { kitaev@213: line = line.trim(); kitaev@213: if (line.length() == 0) { kitaev@213: continue; kitaev@213: } kitaev@213: if (line.length() < 40+2 /*nodeid, space and at least single-char tagname*/) { kitaev@213: System.out.println("Bad tags line:" + line); // FIXME log or otherwise report (IStatus analog?) kitaev@213: continue; kitaev@213: } kitaev@213: int spacePos = line.indexOf(' '); kitaev@213: if (spacePos != -1) { kitaev@213: assert spacePos == 40; kitaev@213: final byte[] nodeidBytes = line.substring(0, spacePos).getBytes(); kitaev@213: Nodeid nid = Nodeid.fromAscii(nodeidBytes, 0, nodeidBytes.length); kitaev@213: String tagName = line.substring(spacePos+1); kitaev@213: List nids = name2nid.get(tagName); kitaev@213: if (nids == null) { kitaev@213: nids = new LinkedList(); kitaev@213: // tagName is substring of full line, thus need a copy to let the line be GC'ed kitaev@213: // new String(tagName.toCharArray()) is more expressive, but results in 1 extra arraycopy kitaev@213: tagName = new String(tagName); kitaev@213: name2nid.put(tagName, nids); kitaev@213: } kitaev@213: // XXX repo.getNodeidCache().nodeid(nid); kitaev@213: ((LinkedList) nids).addFirst(nid); kitaev@213: List revTags = nid2name.get(nid); kitaev@213: if (revTags == null) { kitaev@213: revTags = new LinkedList(); kitaev@213: nid2name.put(nid, revTags); kitaev@213: } kitaev@213: revTags.add(tagName); kitaev@213: } else { kitaev@213: System.out.println("Bad tags line:" + line); // FIXME see above kitaev@213: } kitaev@213: } kitaev@213: } kitaev@213: kitaev@213: public List tags(Nodeid nid) { kitaev@213: ArrayList rv = new ArrayList(5); kitaev@213: List l; kitaev@213: if ((l = localToName.get(nid)) != null) { kitaev@213: rv.addAll(l); kitaev@213: } kitaev@213: if ((l = globalToName.get(nid)) != null) { kitaev@213: rv.addAll(l); kitaev@213: } kitaev@213: return rv; kitaev@213: } kitaev@213: kitaev@213: public boolean isTagged(Nodeid nid) { kitaev@213: return localToName.containsKey(nid) || globalToName.containsKey(nid); kitaev@213: } kitaev@213: kitaev@213: public List tagged(String tagName) { kitaev@213: ArrayList rv = new ArrayList(5); kitaev@213: List l; kitaev@213: if ((l = localFromName.get(tagName)) != null) { kitaev@213: rv.addAll(l); kitaev@213: } kitaev@213: if ((l = globalFromName.get(tagName)) != null) { kitaev@213: rv.addAll(l); kitaev@213: } kitaev@213: return rv; kitaev@213: } kitaev@213: }