/*******************************************************************************
 * Copyright (c) 2004, 2018 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.core.internal.registry;

/**
 * An object which has the general characteristics of all the nestable elements
 * in a plug-in manifest.
 */
public abstract class RegistryObject implements KeyedElement {
	//Object identifier
	private int objectId = RegistryObjectManager.UNKNOWN;
	//The children of the element
	protected int[] children = RegistryObjectManager.EMPTY_INT_ARRAY;

	// The field combines offset, persistence flag, and no offset flag
	private int extraDataOffset = EMPTY_MASK;

	// it is assumed that int has 32 bits (bits #0 to #31);
	// bits #0 - #29 are the offset (limited to about 1Gb)
	// bit #30 - persistance flag
	// bit #31 - registry object has no extra data offset
	// the bit#31 is a sign bit; bit#30 is the highest mantissa bit
	static final int EMPTY_MASK = 0x80000000; // only taking bit #31
	static final int PERSIST_MASK = 0x40000000; // only taking bit #30
	static final int OFFSET_MASK = 0x3FFFFFFF; // all bits but #30, #31

	//The registry that owns this object
	protected ExtensionRegistry registry;

	protected RegistryObject(ExtensionRegistry registry, boolean persist) {
		this.registry = registry;
		setPersist(persist);
	}

	void setRawChildren(int[] values) {
		children = values;
	}

	//This can not return null. It returns the singleton empty array or an array
	protected int[] getRawChildren() {
		return children;
	}

	void setObjectId(int value) {
		objectId = value;
	}

	protected int getObjectId() {
		return objectId;
	}

	//Implementation of the KeyedElement interface
	@Override
	public int getKeyHashCode() {
		return objectId;
	}

	@Override
	public Object getKey() {
		return Integer.valueOf(objectId);
	}

	@Override
	public boolean compare(KeyedElement other) {
		return objectId == ((RegistryObject) other).objectId;
	}

	protected boolean shouldPersist() {
		return (extraDataOffset & PERSIST_MASK) == PERSIST_MASK;
	}

	private void setPersist(boolean persist) {
		if (persist)
			extraDataOffset |= PERSIST_MASK;
		else
			extraDataOffset &= ~PERSIST_MASK;
	}

	protected boolean noExtraData() {
		return (extraDataOffset & EMPTY_MASK) == EMPTY_MASK;
	}

	// Convert no extra data to -1 on output
	protected int getExtraDataOffset() {
		if (noExtraData())
			return -1;
		return extraDataOffset & OFFSET_MASK;
	}

	// Accept -1 as "no extra data" on input
	protected void setExtraDataOffset(int offset) {
		if (offset == -1) {
			extraDataOffset &= ~OFFSET_MASK; // clear all offset bits
			extraDataOffset |= EMPTY_MASK;
			return;
		}

		if ((offset & OFFSET_MASK) != offset)
			throw new IllegalArgumentException("Registry object: extra data offset is out of range"); //$NON-NLS-1$

		extraDataOffset &= ~(OFFSET_MASK | EMPTY_MASK); // clear all offset bits; mark as non-empty
		extraDataOffset |= (offset & OFFSET_MASK); // set all offset bits
	}

	protected String getLocale() {
		return registry.getLocale();
	}
}
