changeset 104:54562de502f7

Preliminary tags implementation
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Fri, 28 Jan 2011 17:51:54 +0100
parents 0b2dcca7de9f
children 0617dd31477f
files src/org/tmatesoft/hg/repo/HgRepository.java src/org/tmatesoft/hg/repo/HgTags.java
diffstat 2 files changed, 121 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/repo/HgRepository.java	Fri Jan 28 04:57:46 2011 +0100
+++ b/src/org/tmatesoft/hg/repo/HgRepository.java	Fri Jan 28 17:51:54 2011 +0100
@@ -119,6 +119,12 @@
 	public final HgTags getTags() {
 		if (tags == null) {
 			tags = new HgTags();
+			try {
+				tags.readGlobal(new File(repoDir.getParentFile(), ".hgtags"));
+				tags.readLocal(new File(repoDir, "localtags"));
+			} catch (IOException ex) {
+				ex.printStackTrace(); // FIXME log or othewise report
+			}
 		}
 		return tags;
 	}
--- a/src/org/tmatesoft/hg/repo/HgTags.java	Fri Jan 28 04:57:46 2011 +0100
+++ b/src/org/tmatesoft/hg/repo/HgTags.java	Fri Jan 28 17:51:54 2011 +0100
@@ -16,25 +16,135 @@
  */
 package org.tmatesoft.hg.repo;
 
-import java.util.Collections;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
 
 import org.tmatesoft.hg.core.Nodeid;
 
 /**
- * FIXME Place-holder, implement
+ * @see http://mercurial.selenic.com/wiki/TagDesign
  *
  * @author Artem Tikhomirov
  * @author TMate Software Ltd.
  */
 public class HgTags {
+	// global tags come from ".hgtags"
+	// local come from ".hg/localtags"
+
+	private final Map<Nodeid, List<String>> globalToName;
+	private final Map<Nodeid, List<String>> localToName;
+	private final Map<String, List<Nodeid>> globalFromName;
+	private final Map<String, List<Nodeid>> localFromName;
+	
+	
+	/*package-local*/ HgTags() {
+		globalToName =  new HashMap<Nodeid, List<String>>();
+		localToName  =  new HashMap<Nodeid, List<String>>();
+		globalFromName = new TreeMap<String, List<Nodeid>>();
+		localFromName  = new TreeMap<String, List<Nodeid>>();
+	}
+	
+	/*package-local*/ void readLocal(File localTags) throws IOException {
+		if (localTags == null || localTags.isDirectory()) {
+			throw new IllegalArgumentException(String.valueOf(localTags));
+		}
+		read(localTags, localToName, localFromName);
+	}
+	
+	/*package-local*/ void readGlobal(File globalTags) throws IOException {
+		if (globalTags == null || globalTags.isDirectory()) {
+			throw new IllegalArgumentException(String.valueOf(globalTags));
+		}
+		read(globalTags, globalToName, globalFromName);
+	}
+	
+	private void read(File f, Map<Nodeid,List<String>> nid2name, Map<String, List<Nodeid>> name2nid) throws IOException {
+		if (!f.canRead()) {
+			return;
+		}
+		BufferedReader r = null;
+		try {
+			r = new BufferedReader(new FileReader(f));
+			read(r, nid2name, name2nid);
+		} finally {
+			if (r != null) {
+				r.close();
+			}
+		}
+	}
+	
+	private void read(BufferedReader reader, Map<Nodeid,List<String>> nid2name, Map<String, List<Nodeid>> name2nid) throws IOException {
+		String line;
+		while ((line = reader.readLine()) != null) {
+			line = line.trim();
+			if (line.length() == 0) {
+				continue;
+			}
+			if (line.length() < 40+2 /*nodeid, space and at least single-char tagname*/) {
+				System.out.println("Bad tags line:" + line); // FIXME log or otherwise report (IStatus analog?) 
+				continue;
+			}
+			int spacePos = line.indexOf(' ');
+			if (spacePos != -1) {
+				assert spacePos == 40;
+				final byte[] nodeidBytes = line.substring(0, spacePos).getBytes();
+				Nodeid nid = Nodeid.fromAscii(nodeidBytes, 0, nodeidBytes.length);
+				String tagName = line.substring(spacePos+1);
+				List<Nodeid> nids = name2nid.get(tagName);
+				if (nids == null) {
+					nids = new LinkedList<Nodeid>();
+					// tagName is substring of full line, thus need a copy to let the line be GC'ed
+					// new String(tagName.toCharArray()) is more expressive, but results in 1 extra arraycopy
+					tagName = new String(tagName);
+					name2nid.put(tagName, nids);
+				}
+				// XXX repo.getNodeidCache().nodeid(nid);
+				((LinkedList<Nodeid>) nids).addFirst(nid);
+				List<String> revTags = nid2name.get(nid);
+				if (revTags == null) {
+					revTags = new LinkedList<String>();
+					nid2name.put(nid, revTags);
+				}
+				revTags.add(tagName);
+			} else {
+				System.out.println("Bad tags line:" + line); // FIXME see above
+			}
+		}
+	}
 
 	public List<String> tags(Nodeid nid) {
-		return Collections.emptyList();
+		ArrayList<String> rv = new ArrayList<String>(5);
+		List<String> l;
+		if ((l = localToName.get(nid)) != null) {
+			rv.addAll(l);
+		}
+		if ((l = globalToName.get(nid)) != null) {
+			rv.addAll(l);
+		}
+		return rv;
 	}
 
 	public boolean isTagged(Nodeid nid) {
-		// TODO implement
-		return false;
+		return localToName.containsKey(nid) || globalToName.containsKey(nid);
+	}
+
+	public List<Nodeid> tagged(String tagName) {
+		ArrayList<Nodeid> rv = new ArrayList<Nodeid>(5);
+		List<Nodeid> l;
+		if ((l = localFromName.get(tagName)) != null) {
+			rv.addAll(l);
+		}
+		if ((l = globalFromName.get(tagName)) != null) {
+			rv.addAll(l);
+		}
+		return rv;
 	}
 }