/******************************************************************************
 * Copyright (c) 2006, 2010 VMware Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Apache License v2.0 which accompanies this distribution. 
 * The Eclipse Public License is available at 
 * http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0
 * is available at http://www.opensource.org/licenses/apache2.0.php.
 * You may elect to redistribute this code under either of these licenses. 
 * 
 * Contributors:
 *   VMware Inc.
 *****************************************************************************/

package org.eclipse.gemini.blueprint.io.internal;

import java.util.ArrayList;
import java.util.List;

import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
import org.springframework.util.StringUtils;

/**
 * Utility class for handling various header operations such as splitting a
 * manifest header into packages or extracting the version from a certain entry.
 * 
 * @author Costin Leau
 * 
 */
public abstract class OsgiHeaderUtils {

	private static final char ROUND_BRACKET_CHAR = '(';

	private static final char SQUARE_BRACKET_CHAR = '[';

	private static final char QUOTE_CHAR = '\"';

	private static final char COMMA_CHAR = ',';

	private static final String SEMI_COLON = ";";
	private static final String DOUBLE_QUOTE = "\"";
	private static final String DEFAULT_VERSION = "0.0.0";


	public static String[] getBundleClassPath(Bundle bundle) {
		return getHeaderAsTrimmedStringArray(bundle, Constants.BUNDLE_CLASSPATH);
	}

	public static String[] getRequireBundle(Bundle bundle) {
		return getHeaderWithAttributesAsTrimmedStringArray(bundle, Constants.REQUIRE_BUNDLE);
	}

	private static String[] getHeaderAsTrimmedStringArray(Bundle bundle, String header) {
		if (bundle == null || !StringUtils.hasText(header))
			return new String[0];

		String headerContent = (String) bundle.getHeaders().get(header);
		String[] entries = StringUtils.commaDelimitedListToStringArray(headerContent);
		for (int i = 0; i < entries.length; i++) {
			entries[i] = entries[i].trim();
		}

		return entries;
	}

	private static String[] getHeaderWithAttributesAsTrimmedStringArray(Bundle bundle, String header) {
		if (bundle == null || !StringUtils.hasText(header))
			return new String[0];

		String headerContent = (String) bundle.getHeaders().get(header);

		if (!StringUtils.hasText(headerContent))
			return new String[0];

		// consider , as a delimiter only if a quote is not encountered
		List<String> tokens = new ArrayList<String>(2);

		StringBuilder token = new StringBuilder();
		boolean ignoreComma = false;
		for (int stringIndex = 0; stringIndex < headerContent.length(); stringIndex++) {
			char currentChar = headerContent.charAt(stringIndex);
			if (currentChar == COMMA_CHAR) {
				if (ignoreComma) {
					token.append(currentChar);
				}
				else {
					tokens.add(token.toString().trim());
					token.delete(0, token.length());
					ignoreComma = false;
				}
			}
			else {
				if (currentChar == QUOTE_CHAR) {
					ignoreComma = !ignoreComma;
				}
				token.append(currentChar);
			}
		}
		tokens.add(token.toString().trim());
		return tokens.toArray(new String[tokens.size()]);
	}

	/**
	 * Parses the required bundle entry to determine the bundle symbolic name
	 * and version.
	 * 
	 * @param string required bundle entry
	 * @return returns an array of strings with 2 entries, the first being the
	 *         bundle sym name, the second the version (or 0.0.0 if nothing is
	 *         specified).
	 */
	public static String[] parseRequiredBundleString(String entry) {
		String[] value = new String[2];

		// determine the bundle symbolic name
		int index = entry.indexOf(SEMI_COLON);

		// there is at least one flag so extract the sym name
		if (index > 0) {
			value[0] = entry.substring(0, index);
		}
		// no flag, short circuit
		else {
			value[0] = entry;
			value[1] = DEFAULT_VERSION;
			return value;
		}

		// look for bundle-version
		index = entry.indexOf(Constants.BUNDLE_VERSION_ATTRIBUTE);
		if (index > 0) {
			// skip the =
			int firstQuoteIndex = index + Constants.BUNDLE_VERSION_ATTRIBUTE.length() + 1;
			// check if the version is quoted
			boolean isQuoted = entry.charAt(firstQuoteIndex) == QUOTE_CHAR;

			// no quote means automatically no range
			if (!isQuoted) {
				int nextAttribute = entry.indexOf(SEMI_COLON, firstQuoteIndex);
				value[1] = (nextAttribute > -1 ? entry.substring(firstQuoteIndex, nextAttribute)
						: entry.substring(firstQuoteIndex));
			}
			else {
				// check if a range or a number is specified
				char testChar = entry.charAt(firstQuoteIndex + 1);
				boolean isRange = (testChar == SQUARE_BRACKET_CHAR || testChar == ROUND_BRACKET_CHAR);
				int secondQuoteStartIndex = (isRange ? firstQuoteIndex + 4 : firstQuoteIndex + 1);

				int numberStart = (isRange ? firstQuoteIndex + 2 : firstQuoteIndex + 1);
				int numberEnd = entry.indexOf(DOUBLE_QUOTE, secondQuoteStartIndex) - (isRange ? 1 : 0);

				value[1] = entry.substring(numberStart, numberEnd);
				// if it's a range, append the interval notation
				if (isRange) {
					value[1] = entry.charAt(firstQuoteIndex + 1) + value[1] + entry.charAt(numberEnd);
				}
			}
		}
		else {
			value[1] = DEFAULT_VERSION;
		}

		return value;
	}
}