/*******************************************************************************
 * Copyright (c) 2000, 2006 IBM Corporation and others.
 * 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.core.search.matching;

import java.io.IOException;

import org.eclipse.jdt.core.compiler.*;
import org.eclipse.jdt.core.search.*;
import org.eclipse.jdt.internal.core.search.indexing.IIndexConstants;
import org.eclipse.jdt.internal.core.index.*;

public class SuperTypeReferencePattern extends JavaSearchPattern implements IIndexConstants {

public char[] superQualification;
public char[] superSimpleName;
public char superClassOrInterface;

// set to CLASS_SUFFIX for only matching classes 
// set to INTERFACE_SUFFIX for only matching interfaces
// set to TYPE_SUFFIX for matching both classes and interfaces
public char typeSuffix; 
public char[] pkgName;
public char[] simpleName;
public char[] enclosingTypeName;
public char classOrInterface;
public int modifiers;
public char[][] typeParameterSignatures;

protected int superRefKind;
public static final int ALL_SUPER_TYPES = 0;
public static final int ONLY_SUPER_INTERFACES = 1; // used for IMPLEMENTORS
public static final int ONLY_SUPER_CLASSES = 2; // used for hierarchy with a class focus

protected static char[][] CATEGORIES = { SUPER_REF };

public static char[] createIndexKey(
	int modifiers,
	char[] packageName,
	char[] typeName,
	char[][] enclosingTypeNames,
	char[][] typeParameterSignatures,
	char classOrInterface,
	char[] superTypeName,
	char superClassOrInterface) {

	if (superTypeName == null)
		superTypeName = OBJECT;
	char[] superSimpleName = CharOperation.lastSegment(superTypeName, '.');
	char[] superQualification = null;
	if (superSimpleName != superTypeName) {
		int length = superTypeName.length - superSimpleName.length - 1;
		superQualification = new char[length];
		System.arraycopy(superTypeName, 0, superQualification, 0, length);
	}

	// if the supertype name contains a $, then split it into: source name and append the $ prefix to the qualification
	//	e.g. p.A$B ---> p.A$ + B
	char[] superTypeSourceName = CharOperation.lastSegment(superSimpleName, '$');
	if (superTypeSourceName != superSimpleName) {
		int start = superQualification == null ? 0 : superQualification.length + 1;
		int prefixLength = superSimpleName.length - superTypeSourceName.length;
		char[] mangledQualification = new char[start + prefixLength];
		if (superQualification != null) {
			System.arraycopy(superQualification, 0, mangledQualification, 0, start-1);
			mangledQualification[start-1] = '.';
		}
		System.arraycopy(superSimpleName, 0, mangledQualification, start, prefixLength);
		superQualification = mangledQualification;
		superSimpleName = superTypeSourceName;
	}

	char[] simpleName = CharOperation.lastSegment(typeName, '.');
	char[] enclosingTypeName = CharOperation.concatWith(enclosingTypeNames, '$');
	if (superQualification != null && CharOperation.equals(superQualification, packageName))
		packageName = ONE_ZERO; // save some space
	
	char[] typeParameters = CharOperation.NO_CHAR;
	int typeParametersLength = 0;
	if (typeParameterSignatures != null) {
		StringBuffer buffer = new StringBuffer();
		for (int i = 0, length = typeParameterSignatures.length; i < length; i++) {
			char[] typeParameter = typeParameterSignatures[i];
			buffer.append(typeParameter);
			typeParametersLength += typeParameter.length;
			if (i != length-1) {
				buffer.append(',');
				typeParametersLength++;
			}
		}
		typeParameters = new char[typeParametersLength];
		buffer.getChars(0, typeParametersLength, typeParameters, 0);
	}

	// superSimpleName / superQualification / simpleName / enclosingTypeName / typeParameters / packageName / superClassOrInterface classOrInterface modifiers
	int superLength = superSimpleName == null ? 0 : superSimpleName.length;
	int superQLength = superQualification == null ? 0 : superQualification.length;
	int simpleLength = simpleName == null ? 0 : simpleName.length;
	int enclosingLength = enclosingTypeName == null ? 0 : enclosingTypeName.length;
	int packageLength = packageName == null ? 0 : packageName.length;
	char[] result = new char[superLength + superQLength + simpleLength + enclosingLength + typeParametersLength + packageLength + 9];
	int pos = 0;
	if (superLength > 0) {
		System.arraycopy(superSimpleName, 0, result, pos, superLength);
		pos += superLength;
	}
	result[pos++] = SEPARATOR;
	if (superQLength > 0) {
		System.arraycopy(superQualification, 0, result, pos, superQLength);
		pos += superQLength;
	}
	result[pos++] = SEPARATOR;
	if (simpleLength > 0) {
		System.arraycopy(simpleName, 0, result, pos, simpleLength);
		pos += simpleLength;
	}
	result[pos++] = SEPARATOR;
	if (enclosingLength > 0) {
		System.arraycopy(enclosingTypeName, 0, result, pos, enclosingLength);
		pos += enclosingLength;
	}
	result[pos++] = SEPARATOR;
	if (typeParametersLength > 0) {
		System.arraycopy(typeParameters, 0, result, pos, typeParametersLength);
		pos += typeParametersLength;
	}
	result[pos++] = SEPARATOR;
	if (packageLength > 0) {
		System.arraycopy(packageName, 0, result, pos, packageLength);
		pos += packageLength;
	}
	result[pos++] = SEPARATOR;
	result[pos++] = superClassOrInterface;
	result[pos++] = classOrInterface;
	result[pos] = (char) modifiers;
	return result;
}

public SuperTypeReferencePattern(
	char[] superQualification,
	char[] superSimpleName,
	int superRefKind,
	int matchRule) {

	this(matchRule);

	this.superQualification = isCaseSensitive() ? superQualification : CharOperation.toLowerCase(superQualification);
	this.superSimpleName = (isCaseSensitive() || isCamelCase())  ? superSimpleName : CharOperation.toLowerCase(superSimpleName);
	((InternalSearchPattern)this).mustResolve = superQualification != null;
	this.superRefKind = superRefKind;
}
public SuperTypeReferencePattern(
	char[] superQualification,
	char[] superSimpleName,
	int superRefKind,
	char typeSuffix,
	int matchRule) {

	this(superQualification, superSimpleName, superRefKind, matchRule);
	this.typeSuffix = typeSuffix;
	((InternalSearchPattern)this).mustResolve = superQualification != null || typeSuffix != IIndexConstants.TYPE_SUFFIX;
}
SuperTypeReferencePattern(int matchRule) {
	super(SUPER_REF_PATTERN, matchRule);
}
/*
 * superSimpleName / superQualification / simpleName / enclosingTypeName / typeParameters / pkgName / superClassOrInterface classOrInterface modifiers
 */
public void decodeIndexKey(char[] key) {
	int slash = CharOperation.indexOf(SEPARATOR, key, 0);
	this.superSimpleName = CharOperation.subarray(key, 0, slash);

	// some values may not have been know when indexed so decode as null
	int start = slash + 1;
	slash = CharOperation.indexOf(SEPARATOR, key, start);
	this.superQualification = slash == start ? null : CharOperation.subarray(key, start, slash);

	slash = CharOperation.indexOf(SEPARATOR, key, start = slash + 1);
	this.simpleName = CharOperation.subarray(key, start, slash);

	start = ++slash;
	if (key[start] == SEPARATOR) {
		this.enclosingTypeName = null;
	} else {
		slash = CharOperation.indexOf(SEPARATOR, key, start);
		if (slash == (start+1) && key[start] == ZERO_CHAR) {
			this.enclosingTypeName = ONE_ZERO;
		} else {
			char[] names = CharOperation.subarray(key, start, slash);
			this.enclosingTypeName = names;
		}
	}

	start = ++slash;
	if (key[start] == SEPARATOR) {
		this.typeParameterSignatures = null;
	} else {
		slash = CharOperation.indexOf(SEPARATOR, key, start);
		this.typeParameterSignatures = CharOperation.splitOn(',', key, start, slash);
	}

	start = ++slash;
	if (key[start] == SEPARATOR) {
		this.pkgName = null;
	} else {
		slash = CharOperation.indexOf(SEPARATOR, key, start);
		if (slash == (start+1) && key[start] == ZERO_CHAR) {
			this.pkgName = this.superQualification;
		} else {
			char[] names = CharOperation.subarray(key, start, slash);
			this.pkgName = names;
		}
	}

	this.superClassOrInterface = key[slash + 1];
	this.classOrInterface = key[slash + 2];
	this.modifiers = key[slash + 3]; // implicit cast to int type
}
public SearchPattern getBlankPattern() {
	return new SuperTypeReferencePattern(R_EXACT_MATCH | R_CASE_SENSITIVE);
}
public char[][] getIndexCategories() {
	return CATEGORIES;
}
public boolean matchesDecodedKey(SearchPattern decodedPattern) {
	SuperTypeReferencePattern pattern = (SuperTypeReferencePattern) decodedPattern;
	if (this.superRefKind == ONLY_SUPER_CLASSES && pattern.enclosingTypeName != IIndexConstants.ONE_ZERO/*not an anonymous*/) 
		// consider enumerations as classes, reject interfaces and annotations
		if (pattern.superClassOrInterface == IIndexConstants.INTERFACE_SUFFIX 
			|| pattern.superClassOrInterface == IIndexConstants.ANNOTATION_TYPE_SUFFIX) 
			return false;

	if (pattern.superQualification != null)
		if (!matchesName(this.superQualification, pattern.superQualification)) return false;

	return matchesName(this.superSimpleName, pattern.superSimpleName);
}
EntryResult[] queryIn(Index index) throws IOException {
	char[] key = this.superSimpleName; // can be null
	int matchRule = getMatchRule();

	// cannot include the superQualification since it may not exist in the index
	switch(getMatchMode()) {
		case R_EXACT_MATCH :
			if (this.isCamelCase) break;
			// do a prefix query with the superSimpleName
			matchRule &= ~R_EXACT_MATCH;
			matchRule |= R_PREFIX_MATCH;
			if (this.superSimpleName != null)
				key = CharOperation.append(this.superSimpleName, SEPARATOR);
			break;
		case R_PREFIX_MATCH :
			// do a prefix query with the superSimpleName
			break;
		case R_PATTERN_MATCH :
			// do a pattern query with the superSimpleName
			break;
		case R_REGEXP_MATCH :
			// TODO (frederic) implement regular expression match
			break;
	}

	return index.query(getIndexCategories(), key, matchRule); // match rule is irrelevant when the key is null
}
protected StringBuffer print(StringBuffer output) {
	switch (this.superRefKind) {
		case ALL_SUPER_TYPES:
			output.append("SuperTypeReferencePattern: <"); //$NON-NLS-1$
			break;
		case ONLY_SUPER_INTERFACES:
			output.append("SuperInterfaceReferencePattern: <"); //$NON-NLS-1$
			break;
		case ONLY_SUPER_CLASSES:
			output.append("SuperClassReferencePattern: <"); //$NON-NLS-1$
			break;
	}
	if (superSimpleName != null) 
		output.append(superSimpleName);
	else
		output.append("*"); //$NON-NLS-1$
	output.append(">"); //$NON-NLS-1$
	return super.print(output);
}
}
