/*******************************************************************************
 * 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.compiler.classfmt;

abstract public class ClassFileStruct {
	byte[] reference;
	int[] constantPoolOffsets;
	int structOffset;

public static String printTypeModifiers(int modifiers) {

	java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
	java.io.PrintWriter print = new java.io.PrintWriter(out);

	if ((modifiers & ClassFileConstants.AccPublic) != 0) print.print("public "); //$NON-NLS-1$
	if ((modifiers & ClassFileConstants.AccPrivate) != 0) print.print("private "); //$NON-NLS-1$
	if ((modifiers & ClassFileConstants.AccFinal) != 0) print.print("final "); //$NON-NLS-1$
	if ((modifiers & ClassFileConstants.AccSuper) != 0) print.print("super "); //$NON-NLS-1$
	if ((modifiers & ClassFileConstants.AccInterface) != 0) print.print("interface "); //$NON-NLS-1$
	if ((modifiers & ClassFileConstants.AccAbstract) != 0) print.print("abstract "); //$NON-NLS-1$
	print.flush();
	return out.toString();
}
public static void verifyMethodNameAndSignature(char[] name, char[] signature) throws ClassFormatException {

	// ensure name is not empty 
	if (name.length == 0) {
		throw new ClassFormatException(ClassFormatException.ErrInvalidMethodName);
	}

	// if name begins with the < character it must be clinit or init
	if (name[0] == '<') {
		if (new String(name).equals("<clinit>") || new String(name).equals("<init>")) { //$NON-NLS-2$ //$NON-NLS-1$
			int signatureLength = signature.length;
			if (!((signatureLength > 2)
				&& (signature[0] == '(')
				&& (signature[signatureLength - 2] == ')')
				&& (signature[signatureLength - 1] == 'V'))) {
				throw new ClassFormatException(ClassFormatException.ErrInvalidMethodSignature);
			}
		} else {
			throw new ClassFormatException(ClassFormatException.ErrInvalidMethodName);
		}
	}
}

public ClassFileStruct(byte[] classFileBytes, int[] offsets, int offset) {
	reference = classFileBytes;
	constantPoolOffsets = offsets;
	structOffset = offset;
}
public double doubleAt(int relativeOffset) {
	return (Double.longBitsToDouble(this.i8At(relativeOffset)));
}
public float floatAt(int relativeOffset) {
	return (Float.intBitsToFloat(this.i4At(relativeOffset)));
}
public int i1At(int relativeOffset) {
	return reference[relativeOffset + structOffset];
}
public int i2At(int relativeOffset) {
	int position = relativeOffset + structOffset;
	return (reference[position++] << 8) | (reference[position] & 0xFF);
}
public int i4At(int relativeOffset) {
	int position = relativeOffset + structOffset;
	return ((reference[position++] & 0xFF) << 24) | ((reference[position++] & 0xFF) << 16) | ((reference[position++] & 0xFF) << 8) + (reference[position] & 0xFF);
}
public long i8At(int relativeOffset) {
	int position = relativeOffset + structOffset;
	return (((long) (reference[position++] & 0xFF)) << 56) 
					| (((long) (reference[position++] & 0xFF)) << 48) 
					| (((long) (reference[position++] & 0xFF)) << 40) 
					| (((long) (reference[position++] & 0xFF)) << 32) 
					| (((long) (reference[position++] & 0xFF)) << 24) 
					| (((long) (reference[position++] & 0xFF)) << 16) 
					| (((long) (reference[position++] & 0xFF)) << 8) 
					| (reference[position++] & 0xFF);
}
protected void reset() {
	this.reference = null;
	this.constantPoolOffsets = null;
}
public int u1At(int relativeOffset) {
	return (reference[relativeOffset + structOffset] & 0xFF);
}
public int u2At(int relativeOffset) {
	int position = relativeOffset + structOffset;
	return ((reference[position++] & 0xFF) << 8) | (reference[position] & 0xFF);
}
public long u4At(int relativeOffset) {
	int position = relativeOffset + structOffset;
	return (((reference[position++] & 0xFFL) << 24) | ((reference[position++] & 0xFF) << 16) | ((reference[position++] & 0xFF) << 8) | (reference[position] & 0xFF));
}
public char[] utf8At(int relativeOffset, int bytesAvailable) {
	int length = bytesAvailable;
	char outputBuf[] = new char[bytesAvailable];
	int outputPos = 0;
	int readOffset = this.structOffset + relativeOffset;
	
	while (length != 0) {
		int x = this.reference[readOffset++] & 0xFF;
		length--;
		if ((0x80 & x) != 0) {
			if ((x & 0x20) != 0) {
				length-=2;
				x = ((x & 0xF) << 12) | ((this.reference[readOffset++] & 0x3F) << 6) | (this.reference[readOffset++] & 0x3F);
			} else {
				length--;
				x = ((x & 0x1F) << 6) | (this.reference[readOffset++] & 0x3F);
			}
		}
		outputBuf[outputPos++] = (char) x;
	}

	if (outputPos != bytesAvailable) {
		System.arraycopy(outputBuf, 0, (outputBuf = new char[outputPos]), 0, outputPos);
	}
	return outputBuf;
}
public char[] utf8At(int relativeOffset, int bytesAvailable, boolean testValidity) throws ClassFormatException {
	int x, y, z;
	int length = bytesAvailable;
	char outputBuf[] = new char[bytesAvailable];
	int outputPos = 0;
	int readOffset = structOffset + relativeOffset;
	
	while (length != 0) {
		x = reference[readOffset++] & 0xFF;
		length--;
		if ((0x80 & x) != 0) {
			if (testValidity) {
				if ((0x40 & x) == 0) {
					throw new ClassFormatException(ClassFormatException.ErrMalformedUtf8);
				}
				if (length < 1) {
					throw new ClassFormatException(ClassFormatException.ErrMalformedUtf8);
				}
			}
			y = this.reference[readOffset++] & 0xFF;
			length--;
			if (testValidity) {
				if ((y & 0xC0) != 0x80) {
					throw new ClassFormatException(ClassFormatException.ErrMalformedUtf8);
				}
			}
			if ((x & 0x20) != 0) {
				if (testValidity && (length < 1)) {
					throw new ClassFormatException(ClassFormatException.ErrMalformedUtf8);
				}
				z = this.reference[readOffset++] & 0xFF;
				length--;
				if (testValidity && ((z & 0xC0) != 0x80)) {
					throw new ClassFormatException(ClassFormatException.ErrMalformedUtf8);
				}
				x = ((x & 0x1F) << 12) + ((y & 0x3F) << 6) + (z & 0x3F);
				if (testValidity && (x < 0x0800)) {
					throw new ClassFormatException(ClassFormatException.ErrMalformedUtf8);
				}
			} else {
				x = ((x & 0x1F) << 6) + (y & 0x3F);
				if (testValidity && !((x == 0) || (x >= 0x80))) {
					throw new ClassFormatException(ClassFormatException.ErrMalformedUtf8);
				}
			}
		} else {
			if (testValidity && x == 0) {
					throw new ClassFormatException(ClassFormatException.ErrMalformedUtf8);
			}
		}
		outputBuf[outputPos++] = (char) x;
	}

	if (outputPos != bytesAvailable) {
		System.arraycopy(outputBuf, 0, (outputBuf = new char[outputPos]), 0, outputPos);
	}
	return outputBuf;
}
}
