tikhomirov@384: /*
tikhomirov@384:  * Copyright (c) 2012 TMate Software Ltd
tikhomirov@384:  *  
tikhomirov@384:  * This program is free software; you can redistribute it and/or modify
tikhomirov@384:  * it under the terms of the GNU General Public License as published by
tikhomirov@384:  * the Free Software Foundation; version 2 of the License.
tikhomirov@384:  *
tikhomirov@384:  * This program is distributed in the hope that it will be useful,
tikhomirov@384:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
tikhomirov@384:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
tikhomirov@384:  * GNU General Public License for more details.
tikhomirov@384:  *
tikhomirov@384:  * For information on how to redistribute this software under
tikhomirov@384:  * the terms of a license other than GNU General Public License
tikhomirov@384:  * contact TMate Software at support@hg4j.com
tikhomirov@384:  */
tikhomirov@384: package org.tmatesoft.hg.tools;
tikhomirov@384: 
tikhomirov@384: import java.util.ArrayList;
tikhomirov@384: import java.util.Collections;
tikhomirov@384: import java.util.Iterator;
tikhomirov@384: import java.util.LinkedHashMap;
tikhomirov@384: import java.util.List;
tikhomirov@384: import java.util.Map;
tikhomirov@384: import java.util.TimeZone;
tikhomirov@384: import java.util.Map.Entry;
tikhomirov@384: 
tikhomirov@384: import org.tmatesoft.hg.core.Nodeid;
tikhomirov@384: 
tikhomirov@384: /**
tikhomirov@384:  *
tikhomirov@384:  * @author Artem Tikhomirov
tikhomirov@384:  * @author TMate Software Ltd.
tikhomirov@384:  */
tikhomirov@384: public class ChangelogEntryBuilder {
tikhomirov@384: 
tikhomirov@384: 	private String user;
tikhomirov@384: 	private List<String> modifiedFiles;
tikhomirov@384: 	private final Map<String, String> extrasMap = new LinkedHashMap<String, String>();
tikhomirov@384: 	private Integer tzOffset;
tikhomirov@384: 	private Long csetTime;
tikhomirov@384: 	
tikhomirov@384: 	public ChangelogEntryBuilder user(String username) {
tikhomirov@384: 		user = username;
tikhomirov@384: 		return this;
tikhomirov@384: 	}
tikhomirov@384: 	
tikhomirov@384: 	public String user() {
tikhomirov@384: 		if (user == null) {
tikhomirov@384: 			// for our testing purposes anything but null is ok. no reason to follow Hg username lookup conventions 
tikhomirov@384: 			user = System.getProperty("user.name");
tikhomirov@384: 		}
tikhomirov@384: 		return user;
tikhomirov@384: 	}
tikhomirov@384: 	
tikhomirov@384: 	public ChangelogEntryBuilder setModified(List<String> files) {
tikhomirov@384: 		modifiedFiles = new ArrayList<String>(files == null ? Collections.<String>emptyList() : files);
tikhomirov@384: 		return this;
tikhomirov@384: 	}
tikhomirov@384: 
tikhomirov@384: 	public ChangelogEntryBuilder addModified(List<String> files) {
tikhomirov@384: 		if (modifiedFiles == null) {
tikhomirov@384: 			return setModified(files);
tikhomirov@384: 		}
tikhomirov@384: 		modifiedFiles.addAll(files);
tikhomirov@384: 		return this;
tikhomirov@384: 	}
tikhomirov@384: 	
tikhomirov@384: 	public ChangelogEntryBuilder branch(String branchName) {
tikhomirov@386: 		if (branchName == null || "default".equals(branchName)) {
tikhomirov@384: 			extrasMap.remove("branch");
tikhomirov@384: 		} else {
tikhomirov@384: 			extrasMap.put("branch", branchName);
tikhomirov@384: 		}
tikhomirov@384: 		return this;
tikhomirov@384: 	}
tikhomirov@384: 	
tikhomirov@384: 	public ChangelogEntryBuilder extras(Map<String, String> extras) {
tikhomirov@384: 		extrasMap.clear();
tikhomirov@384: 		extrasMap.putAll(extras);
tikhomirov@384: 		return this;
tikhomirov@384: 	}
tikhomirov@384: 	
tikhomirov@384: 	public ChangelogEntryBuilder date(long seconds, int timezoneOffset) {
tikhomirov@384: 		csetTime = seconds;
tikhomirov@384: 		tzOffset = timezoneOffset;
tikhomirov@384: 		return this;
tikhomirov@384: 	}
tikhomirov@384: 	
tikhomirov@384: 	private long csetTime() {
tikhomirov@384: 		if (csetTime != null) { 
tikhomirov@384: 			return csetTime;
tikhomirov@384: 		}
tikhomirov@384: 		return System.currentTimeMillis() / 1000;
tikhomirov@384: 	}
tikhomirov@384: 	
tikhomirov@384: 	private int csetTimezone(long time) {
tikhomirov@384: 		if (tzOffset != null) {
tikhomirov@384: 			return tzOffset;
tikhomirov@384: 		}
tikhomirov@384: 		return -(TimeZone.getDefault().getOffset(time) / 1000);
tikhomirov@384: 	}
tikhomirov@384: 
tikhomirov@384: 	public byte[] build(Nodeid manifestRevision, String comment) {
tikhomirov@384: 		String f = "%s\n%s\n%d %d %s\n%s\n\n%s";
tikhomirov@384: 		StringBuilder extras = new StringBuilder();
tikhomirov@384: 		for (Iterator<Entry<String, String>> it = extrasMap.entrySet().iterator(); it.hasNext();) {
tikhomirov@384: 			final Entry<String, String> next = it.next();
tikhomirov@384: 			extras.append(encodeExtrasPair(next.getKey()));
tikhomirov@384: 			extras.append(':');
tikhomirov@384: 			extras.append(encodeExtrasPair(next.getValue()));
tikhomirov@384: 			if (it.hasNext()) {
tikhomirov@384: 				extras.append('\00');
tikhomirov@384: 			}
tikhomirov@384: 		}
tikhomirov@384: 		StringBuilder files = new StringBuilder();
tikhomirov@386: 		if (modifiedFiles != null) {
tikhomirov@386: 			for (Iterator<String> it = modifiedFiles.iterator(); it.hasNext(); ) {
tikhomirov@386: 				files.append(it.next());
tikhomirov@386: 				if (it.hasNext()) {
tikhomirov@386: 					files.append('\n');
tikhomirov@386: 				}
tikhomirov@384: 			}
tikhomirov@384: 		}
tikhomirov@384: 		final long date = csetTime();
tikhomirov@384: 		final int tz = csetTimezone(date);
tikhomirov@386: 		return String.format(f, manifestRevision.toString(), user(), date, tz, extras, files, comment).getBytes();
tikhomirov@384: 	}
tikhomirov@384: 
tikhomirov@384: 	private final static CharSequence encodeExtrasPair(String s) {
tikhomirov@384: 		if (s != null) {
tikhomirov@384: 			return s.replace("\\", "\\\\").replace("\n", "\\n").replace("\r", "\\r").replace("\00", "\\0");
tikhomirov@384: 		}
tikhomirov@384: 		return s;
tikhomirov@384: 	}
tikhomirov@384: }