<commit_before>/***************************************************************************
 * Copyright (c) 2004, 2005, 2006 <NAME>, Germany.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *    <NAME> - initial API and implementation
 **************************************************************************/
package org.eclipse.mylar.internal.team.template;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.Platform;
import org.eclipse.mylar.context.core.MylarStatusHandler;
import org.eclipse.mylar.tasks.core.ITask;
import org.eclipse.mylar.team.AbstractCommitTemplateVariable;
import org.eclipse.mylar.team.MylarTeamPlugin;

/**
 * @author <NAME>
 * @author <NAME>
 */
public class CommitTemplateManager {

	private static final String ATTR_CLASS = "class";

	private static final String ATTR_DESCRIPTION = "description";

	private static final String ATTR_RECOGNIZED_KEYWORD = "recognizedKeyword";

	private static final String ELEM_TEMPLATE_HANDLER = "templateVariable";

	private static final String EXT_POINT_TEMPLATE_HANDLERS = "commitTemplates";
	
	public String generateComment(ITask task, String template) {
		return processKeywords(task, template);
	}

	public String getTaskIdFromCommentOrLabel(String commentOrLabel) {
		String id = getTaskIdFromComment(commentOrLabel);
		return id;
	}

	private String getTaskIdFromComment(String comment) {
		try {
			String template = MylarTeamPlugin.getDefault().getPreferenceStore().getString(
					MylarTeamPlugin.COMMIT_TEMPLATE);
			int templateNewline = template.indexOf('\n');
			String templateFirstLineIndex = template;
			if (templateNewline != -1) {
				templateFirstLineIndex = template.substring(0, templateNewline-1);
			}
			
			String regex = getTaskIdRegEx(templateFirstLineIndex);

			int commentNewlineIndex = comment.indexOf('\n');
			String commentFirstLine = comment;
			if (commentNewlineIndex != -1) {
				commentFirstLine = comment.substring(0, commentNewlineIndex);
			}
			
			Pattern pattern = Pattern.compile(regex);
			Matcher matcher = pattern.matcher(commentFirstLine);
						
			if (matcher.find()) {
				return matcher.group(1);
			}
		} catch (Exception ex) {
			MylarStatusHandler.log(ex, "Problem while parsing task id from comment");
		}

		return null;
	}

	public String getTaskIdRegEx(String template) { 
		final String META_CHARS = " $()*+.< [\\]^{|}";
		final String TASK_ID_PLACEHOLDER = "\uffff";
		final String KEYWORD_PLACEHOLDER = "\ufffe";

		template = template.replaceFirst("\\$\\{task\\.id\\}", TASK_ID_PLACEHOLDER);
		template = replaceKeywords(template, KEYWORD_PLACEHOLDER);
		template = quoteChars(template, META_CHARS);
		template = template.replaceFirst(TASK_ID_PLACEHOLDER, "(\\\\d+)");
		template = template.replaceAll(KEYWORD_PLACEHOLDER, ".*");
		return template;
	}

	private String replaceKeywords(String str, String placeholder) {
		String[] recognizedKeywords = getRecognizedKeywords();
		for (String keyword : recognizedKeywords) {
			str = str.replaceAll("\\$\\{" + keyword + "\\}", placeholder);
		}
		return str;
	}

	private String quoteChars(String str, String charsToQuote) {
		StringBuilder builder = new StringBuilder(str.length() * 2);
		for (int i = 0; i < str.length(); i++) {
			char c = str.charAt(i);
			if (charsToQuote.indexOf(c) != -1) {
				builder.append('\\');
			}
			builder.append(c);
		}
		return builder.toString();
	}

	public String[] getRecognizedKeywords() {
		final ArrayList<String> result = new ArrayList<String>();
		new ExtensionProcessor() {
			@Override
			protected Object processContribution(IConfigurationElement element, String keyword, String description,
					String className) throws Exception {
				result.add(keyword);
				return null;
			}
		}.run();

		return result.toArray(new String[result.size()]);
	}

	public String getHandlerDescription(final String keyword) {
		return (String) new ExtensionProcessor() {
			@Override
			protected Object processContribution(IConfigurationElement element, String foundKeyword,
					String description, String className) throws Exception {
				return keyword.equals(foundKeyword) ? description : null;
			}
		}.run();
	}

	public AbstractCommitTemplateVariable createHandler(final String keyword) {
		return (AbstractCommitTemplateVariable) new ExtensionProcessor() {
			@Override
			protected Object processContribution(IConfigurationElement element, String foundKeyword,
					String description, String className) throws Exception {
				if (keyword.equals(foundKeyword)) {
					AbstractCommitTemplateVariable handler = (AbstractCommitTemplateVariable) element.createExecutableExtension(ATTR_CLASS);
					if (handler instanceof AbstractCommitTemplateVariable) {
						(handler).setDescription(description);
						(handler).setRecognizedKeyword(foundKeyword);
					} else {
						String recognizedKeyword = handler.getRecognizedKeyword();
						if (recognizedKeyword == null || !recognizedKeyword.equals(foundKeyword)) {
							throw new IllegalArgumentException("Keyword markup does not match handler implementation");
						}
					}

					return handler;
				}

				return null;
			}
		}.run();
	}

	private String processKeywords(ITask task, String template) {
		String[] segments = template.split("\\$\\{");
		StringBuffer buffer = new StringBuffer(segments[0]);

		for (int i = 1; i < segments.length; i++) {
			String segment = segments[i];
			String value = null;
			int brace = segment.indexOf('}');
			if (brace > 0) {
				String keyword = segment.substring(0, brace);
				value = processKeyword(task, keyword);
			}

			if (value != null) {
				buffer.append(value);
				buffer.append(segment.substring(brace + 1));
			} 
//			else {
//				buffer.append("${");
//				buffer.append(segment);
//			}
		}
		return buffer.toString();
	}<commit_msg>RESOLVED - bug 166539: [${task.id}] ${task.description} gives odd commit comment default when working without a bug # https://bugs.eclipse.org/bugs/show_bug.cgi?id=166539<commit_after>/***************************************************************************
 * Copyright (c) 2004, 2005, 2006 <NAME>, Germany.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *    <NAME> - initial API and implementation
 **************************************************************************/
package org.eclipse.mylar.internal.team.template;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.Platform;
import org.eclipse.mylar.context.core.MylarStatusHandler;
import org.eclipse.mylar.tasks.core.ITask;
import org.eclipse.mylar.team.AbstractCommitTemplateVariable;
import org.eclipse.mylar.team.MylarTeamPlugin;

/**
 * @author <NAME>
 * @author <NAME>
 */
public class CommitTemplateManager {

	private static final String ATTR_CLASS = "class";

	private static final String ATTR_DESCRIPTION = "description";

	private static final String ATTR_RECOGNIZED_KEYWORD = "recognizedKeyword";

	private static final String ELEM_TEMPLATE_HANDLER = "templateVariable";

	private static final String EXT_POINT_TEMPLATE_HANDLERS = "commitTemplates";
	
	public String generateComment(ITask task, String template) {
		return processKeywords(task, template);
	}

	public String getTaskIdFromCommentOrLabel(String commentOrLabel) {
		String id = getTaskIdFromComment(commentOrLabel);
		return id;
	}

	private String getTaskIdFromComment(String comment) {
		try {
			String template = MylarTeamPlugin.getDefault().getPreferenceStore().getString(
					MylarTeamPlugin.COMMIT_TEMPLATE);
			int templateNewline = template.indexOf('\n');
			String templateFirstLineIndex = template;
			if (templateNewline != -1) {
				templateFirstLineIndex = template.substring(0, templateNewline-1);
			}
			
			String regex = getTaskIdRegEx(templateFirstLineIndex);

			int commentNewlineIndex = comment.indexOf('\n');
			String commentFirstLine = comment;
			if (commentNewlineIndex != -1) {
				commentFirstLine = comment.substring(0, commentNewlineIndex);
			}
			
			Pattern pattern = Pattern.compile(regex);
			Matcher matcher = pattern.matcher(commentFirstLine);
						
			if (matcher.find()) {
				return matcher.group(1);
			}
		} catch (Exception ex) {
			MylarStatusHandler.log(ex, "Problem while parsing task id from comment");
		}

		return null;
	}

	public String getTaskIdRegEx(String template) { 
		final String META_CHARS = " $()*+.< [\\]^{|}";
		final String TASK_ID_PLACEHOLDER = "\uffff";
		final String KEYWORD_PLACEHOLDER = "\ufffe";

		template = template.replaceFirst("\\$\\{task\\.id\\}", TASK_ID_PLACEHOLDER);
		template = replaceKeywords(template, KEYWORD_PLACEHOLDER);
		template = quoteChars(template, META_CHARS);
		template = template.replaceFirst(TASK_ID_PLACEHOLDER, "(\\\\d+)");
		template = template.replaceAll(KEYWORD_PLACEHOLDER, ".*");
		return template;
	}

	private String replaceKeywords(String str, String placeholder) {
		String[] recognizedKeywords = getRecognizedKeywords();
		for (String keyword : recognizedKeywords) {
			str = str.replaceAll("\\$\\{" + keyword + "\\}", placeholder);
		}
		return str;
	}

	private String quoteChars(String str, String charsToQuote) {
		StringBuilder builder = new StringBuilder(str.length() * 2);
		for (int i = 0; i < str.length(); i++) {
			char c = str.charAt(i);
			if (charsToQuote.indexOf(c) != -1) {
				builder.append('\\');
			}
			builder.append(c);
		}
		return builder.toString();
	}

	public String[] getRecognizedKeywords() {
		final ArrayList<String> result = new ArrayList<String>();
		new ExtensionProcessor() {
			@Override
			protected Object processContribution(IConfigurationElement element, String keyword, String description,
					String className) throws Exception {
				result.add(keyword);
				return null;
			}
		}.run();

		return result.toArray(new String[result.size()]);
	}

	public String getHandlerDescription(final String keyword) {
		return (String) new ExtensionProcessor() {
			@Override
			protected Object processContribution(IConfigurationElement element, String foundKeyword,
					String description, String className) throws Exception {
				return keyword.equals(foundKeyword) ? description : null;
			}
		}.run();
	}

	public AbstractCommitTemplateVariable createHandler(final String keyword) {
		return (AbstractCommitTemplateVariable) new ExtensionProcessor() {
			@Override
			protected Object processContribution(IConfigurationElement element, String foundKeyword,
					String description, String className) throws Exception {
				if (keyword.equals(foundKeyword)) {
					AbstractCommitTemplateVariable handler = (AbstractCommitTemplateVariable) element.createExecutableExtension(ATTR_CLASS);
					if (handler instanceof AbstractCommitTemplateVariable) {
						(handler).setDescription(description);
						(handler).setRecognizedKeyword(foundKeyword);
					} else {
						String recognizedKeyword = handler.getRecognizedKeyword();
						if (recognizedKeyword == null || !recognizedKeyword.equals(foundKeyword)) {
							throw new IllegalArgumentException("Keyword markup does not match handler implementation");
						}
					}

					return handler;
				}

				return null;
			}
		}.run();
	}

	private String processKeywords(ITask task, String template) {
		String[] segments = template.split("\\$\\{");
		Stack<String> evaluated = new Stack<String>();
		evaluated.add(segments[0]);

		for (int i = 1; i < segments.length; i++) {
			String segment = segments[i];
			String value = null;
			int brace = segment.indexOf('}');
			if (brace > 0) {
				String keyword = segment.substring(0, brace);
				value = processKeyword(task, keyword);
			}

			if (value != null) {
				evaluated.add(value);
				evaluated.add(segment.substring(brace + 1));
			} else if (!evaluated.isEmpty()) {
				evaluated.pop();
			}
//			else {
//				buffer.append("${");
//				buffer.append(segment);
//			}
		}
		StringBuffer buffer = new StringBuffer();
		for (String string : evaluated) {
			buffer.append(string);
		}
		
 		return buffer.toString();
	}
{'fraction_non_alphanumeric': 0.13632475309122016, 'fraction_numerical': 0.005599191227933743, 'mean_word_length': 4.470012760527435, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 19, 'https://': 1, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2659173', 'n_tokens_mistral': 4232, 'n_tokens_neox': 3636, 'n_words': 881}